roojs-bootstrap.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                 return;
7654             }
7655             valid = false;
7656
7657             if(!target && f.el.isVisible(true)){
7658                 target = f;
7659             }
7660            
7661         });
7662         
7663         if(this.errorMask && !valid){
7664             Roo.bootstrap.Form.popover.mask(this, target);
7665         }
7666         
7667         return valid;
7668     },
7669     
7670     /**
7671      * Returns true if any fields in this form have changed since their original load.
7672      * @return Boolean
7673      */
7674     isDirty : function(){
7675         var dirty = false;
7676         var items = this.getItems();
7677         items.each(function(f){
7678            if(f.isDirty()){
7679                dirty = true;
7680                return false;
7681            }
7682            return true;
7683         });
7684         return dirty;
7685     },
7686      /**
7687      * Performs a predefined action (submit or load) or custom actions you define on this form.
7688      * @param {String} actionName The name of the action type
7689      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7690      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7691      * accept other config options):
7692      * <pre>
7693 Property          Type             Description
7694 ----------------  ---------------  ----------------------------------------------------------------------------------
7695 url               String           The url for the action (defaults to the form's url)
7696 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7697 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7698 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7699                                    validate the form on the client (defaults to false)
7700      * </pre>
7701      * @return {BasicForm} this
7702      */
7703     doAction : function(action, options){
7704         if(typeof action == 'string'){
7705             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7706         }
7707         if(this.fireEvent('beforeaction', this, action) !== false){
7708             this.beforeAction(action);
7709             action.run.defer(100, action);
7710         }
7711         return this;
7712     },
7713
7714     // private
7715     beforeAction : function(action){
7716         var o = action.options;
7717
7718         if(this.loadMask){
7719             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7720         }
7721         // not really supported yet.. ??
7722
7723         //if(this.waitMsgTarget === true){
7724         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7725         //}else if(this.waitMsgTarget){
7726         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7727         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7728         //}else {
7729         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7730        // }
7731
7732     },
7733
7734     // private
7735     afterAction : function(action, success){
7736         this.activeAction = null;
7737         var o = action.options;
7738
7739         //if(this.waitMsgTarget === true){
7740             this.el.unmask();
7741         //}else if(this.waitMsgTarget){
7742         //    this.waitMsgTarget.unmask();
7743         //}else{
7744         //    Roo.MessageBox.updateProgress(1);
7745         //    Roo.MessageBox.hide();
7746        // }
7747         //
7748         if(success){
7749             if(o.reset){
7750                 this.reset();
7751             }
7752             Roo.callback(o.success, o.scope, [this, action]);
7753             this.fireEvent('actioncomplete', this, action);
7754
7755         }else{
7756
7757             // failure condition..
7758             // we have a scenario where updates need confirming.
7759             // eg. if a locking scenario exists..
7760             // we look for { errors : { needs_confirm : true }} in the response.
7761             if (
7762                 (typeof(action.result) != 'undefined')  &&
7763                 (typeof(action.result.errors) != 'undefined')  &&
7764                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7765            ){
7766                 var _t = this;
7767                 Roo.log("not supported yet");
7768                  /*
7769
7770                 Roo.MessageBox.confirm(
7771                     "Change requires confirmation",
7772                     action.result.errorMsg,
7773                     function(r) {
7774                         if (r != 'yes') {
7775                             return;
7776                         }
7777                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7778                     }
7779
7780                 );
7781                 */
7782
7783
7784                 return;
7785             }
7786
7787             Roo.callback(o.failure, o.scope, [this, action]);
7788             // show an error message if no failed handler is set..
7789             if (!this.hasListener('actionfailed')) {
7790                 Roo.log("need to add dialog support");
7791                 /*
7792                 Roo.MessageBox.alert("Error",
7793                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7794                         action.result.errorMsg :
7795                         "Saving Failed, please check your entries or try again"
7796                 );
7797                 */
7798             }
7799
7800             this.fireEvent('actionfailed', this, action);
7801         }
7802
7803     },
7804     /**
7805      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7806      * @param {String} id The value to search for
7807      * @return Field
7808      */
7809     findField : function(id){
7810         var items = this.getItems();
7811         var field = items.get(id);
7812         if(!field){
7813              items.each(function(f){
7814                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7815                     field = f;
7816                     return false;
7817                 }
7818                 return true;
7819             });
7820         }
7821         return field || null;
7822     },
7823      /**
7824      * Mark fields in this form invalid in bulk.
7825      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7826      * @return {BasicForm} this
7827      */
7828     markInvalid : function(errors){
7829         if(errors instanceof Array){
7830             for(var i = 0, len = errors.length; i < len; i++){
7831                 var fieldError = errors[i];
7832                 var f = this.findField(fieldError.id);
7833                 if(f){
7834                     f.markInvalid(fieldError.msg);
7835                 }
7836             }
7837         }else{
7838             var field, id;
7839             for(id in errors){
7840                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7841                     field.markInvalid(errors[id]);
7842                 }
7843             }
7844         }
7845         //Roo.each(this.childForms || [], function (f) {
7846         //    f.markInvalid(errors);
7847         //});
7848
7849         return this;
7850     },
7851
7852     /**
7853      * Set values for fields in this form in bulk.
7854      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7855      * @return {BasicForm} this
7856      */
7857     setValues : function(values){
7858         if(values instanceof Array){ // array of objects
7859             for(var i = 0, len = values.length; i < len; i++){
7860                 var v = values[i];
7861                 var f = this.findField(v.id);
7862                 if(f){
7863                     f.setValue(v.value);
7864                     if(this.trackResetOnLoad){
7865                         f.originalValue = f.getValue();
7866                     }
7867                 }
7868             }
7869         }else{ // object hash
7870             var field, id;
7871             for(id in values){
7872                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7873
7874                     if (field.setFromData &&
7875                         field.valueField &&
7876                         field.displayField &&
7877                         // combos' with local stores can
7878                         // be queried via setValue()
7879                         // to set their value..
7880                         (field.store && !field.store.isLocal)
7881                         ) {
7882                         // it's a combo
7883                         var sd = { };
7884                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7885                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7886                         field.setFromData(sd);
7887
7888                     } else {
7889                         field.setValue(values[id]);
7890                     }
7891
7892
7893                     if(this.trackResetOnLoad){
7894                         field.originalValue = field.getValue();
7895                     }
7896                 }
7897             }
7898         }
7899
7900         //Roo.each(this.childForms || [], function (f) {
7901         //    f.setValues(values);
7902         //});
7903
7904         return this;
7905     },
7906
7907     /**
7908      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7909      * they are returned as an array.
7910      * @param {Boolean} asString
7911      * @return {Object}
7912      */
7913     getValues : function(asString){
7914         //if (this.childForms) {
7915             // copy values from the child forms
7916         //    Roo.each(this.childForms, function (f) {
7917         //        this.setValues(f.getValues());
7918         //    }, this);
7919         //}
7920
7921
7922
7923         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7924         if(asString === true){
7925             return fs;
7926         }
7927         return Roo.urlDecode(fs);
7928     },
7929
7930     /**
7931      * Returns the fields in this form as an object with key/value pairs.
7932      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7933      * @return {Object}
7934      */
7935     getFieldValues : function(with_hidden)
7936     {
7937         var items = this.getItems();
7938         var ret = {};
7939         items.each(function(f){
7940             if (!f.getName()) {
7941                 return;
7942             }
7943             var v = f.getValue();
7944             if (f.inputType =='radio') {
7945                 if (typeof(ret[f.getName()]) == 'undefined') {
7946                     ret[f.getName()] = ''; // empty..
7947                 }
7948
7949                 if (!f.el.dom.checked) {
7950                     return;
7951
7952                 }
7953                 v = f.el.dom.value;
7954
7955             }
7956
7957             // not sure if this supported any more..
7958             if ((typeof(v) == 'object') && f.getRawValue) {
7959                 v = f.getRawValue() ; // dates..
7960             }
7961             // combo boxes where name != hiddenName...
7962             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7963                 ret[f.name] = f.getRawValue();
7964             }
7965             ret[f.getName()] = v;
7966         });
7967
7968         return ret;
7969     },
7970
7971     /**
7972      * Clears all invalid messages in this form.
7973      * @return {BasicForm} this
7974      */
7975     clearInvalid : function(){
7976         var items = this.getItems();
7977
7978         items.each(function(f){
7979            f.clearInvalid();
7980         });
7981
7982
7983
7984         return this;
7985     },
7986
7987     /**
7988      * Resets this form.
7989      * @return {BasicForm} this
7990      */
7991     reset : function(){
7992         var items = this.getItems();
7993         items.each(function(f){
7994             f.reset();
7995         });
7996
7997         Roo.each(this.childForms || [], function (f) {
7998             f.reset();
7999         });
8000
8001
8002         return this;
8003     },
8004     getItems : function()
8005     {
8006         var r=new Roo.util.MixedCollection(false, function(o){
8007             return o.id || (o.id = Roo.id());
8008         });
8009         var iter = function(el) {
8010             if (el.inputEl) {
8011                 r.add(el);
8012             }
8013             if (!el.items) {
8014                 return;
8015             }
8016             Roo.each(el.items,function(e) {
8017                 iter(e);
8018             });
8019
8020
8021         };
8022
8023         iter(this);
8024         return r;
8025
8026
8027
8028
8029     }
8030
8031 });
8032
8033 Roo.apply(Roo.bootstrap.Form, {
8034     
8035     popover : {
8036         
8037         padding : 5,
8038         
8039         isApplied : false,
8040         
8041         isMasked : false,
8042         
8043         form : false,
8044         
8045         target : false,
8046         
8047         toolTip : false,
8048         
8049         intervalID : false,
8050         
8051         maskEl : false,
8052         
8053         apply : function()
8054         {
8055             if(this.isApplied){
8056                 return;
8057             }
8058             
8059             this.maskEl = {
8060                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8061                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8062                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8063                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8064             };
8065             
8066             this.maskEl.top.enableDisplayMode("block");
8067             this.maskEl.left.enableDisplayMode("block");
8068             this.maskEl.bottom.enableDisplayMode("block");
8069             this.maskEl.right.enableDisplayMode("block");
8070             
8071             this.toolTip = new Roo.bootstrap.Tooltip({
8072                 cls : 'roo-form-error-popover',
8073                 alignment : {
8074                     'left' : ['r-l', [-2,0], 'right'],
8075                     'right' : ['l-r', [2,0], 'left'],
8076                     'bottom' : ['tl-bl', [0,2], 'top'],
8077                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8078                 }
8079             });
8080             
8081             this.toolTip.render(Roo.get(document.body));
8082
8083             this.toolTip.el.enableDisplayMode("block");
8084             
8085             Roo.get(document.body).on('click', function(){
8086                 this.unmask();
8087             }, this);
8088             
8089             Roo.get(document.body).on('touchstart', function(){
8090                 this.unmask();
8091             }, this);
8092             
8093             this.isApplied = true
8094         },
8095         
8096         mask : function(form, target)
8097         {
8098             this.form = form;
8099             
8100             this.target = target;
8101             
8102             if(!this.form.errorMask || !target.el){
8103                 return;
8104             }
8105             
8106             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8107             
8108             var ot = this.target.el.calcOffsetsTo(scrollable);
8109             
8110             var scrollTo = ot[1] - this.form.maskOffset;
8111             
8112             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8113             
8114             scrollable.scrollTo('top', scrollTo);
8115             
8116             var box = this.target.el.getBox();
8117             Roo.log(box);
8118             var zIndex = Roo.bootstrap.Modal.zIndex++;
8119
8120             
8121             this.maskEl.top.setStyle('position', 'absolute');
8122             this.maskEl.top.setStyle('z-index', zIndex);
8123             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8124             this.maskEl.top.setLeft(0);
8125             this.maskEl.top.setTop(0);
8126             this.maskEl.top.show();
8127             
8128             this.maskEl.left.setStyle('position', 'absolute');
8129             this.maskEl.left.setStyle('z-index', zIndex);
8130             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8131             this.maskEl.left.setLeft(0);
8132             this.maskEl.left.setTop(box.y - this.padding);
8133             this.maskEl.left.show();
8134
8135             this.maskEl.bottom.setStyle('position', 'absolute');
8136             this.maskEl.bottom.setStyle('z-index', zIndex);
8137             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8138             this.maskEl.bottom.setLeft(0);
8139             this.maskEl.bottom.setTop(box.bottom + this.padding);
8140             this.maskEl.bottom.show();
8141
8142             this.maskEl.right.setStyle('position', 'absolute');
8143             this.maskEl.right.setStyle('z-index', zIndex);
8144             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8145             this.maskEl.right.setLeft(box.right + this.padding);
8146             this.maskEl.right.setTop(box.y - this.padding);
8147             this.maskEl.right.show();
8148
8149             this.toolTip.bindEl = this.target.el;
8150
8151             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8152
8153             var tip = this.target.blankText;
8154
8155             if(this.target.getValue() !== '' ) {
8156                 
8157                 if (this.target.invalidText.length) {
8158                     tip = this.target.invalidText;
8159                 } else if (this.target.regexText.length){
8160                     tip = this.target.regexText;
8161                 }
8162             }
8163
8164             this.toolTip.show(tip);
8165
8166             this.intervalID = window.setInterval(function() {
8167                 Roo.bootstrap.Form.popover.unmask();
8168             }, 10000);
8169
8170             window.onwheel = function(){ return false;};
8171             
8172             (function(){ this.isMasked = true; }).defer(500, this);
8173             
8174         },
8175         
8176         unmask : function()
8177         {
8178             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8179                 return;
8180             }
8181             
8182             this.maskEl.top.setStyle('position', 'absolute');
8183             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8184             this.maskEl.top.hide();
8185
8186             this.maskEl.left.setStyle('position', 'absolute');
8187             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8188             this.maskEl.left.hide();
8189
8190             this.maskEl.bottom.setStyle('position', 'absolute');
8191             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8192             this.maskEl.bottom.hide();
8193
8194             this.maskEl.right.setStyle('position', 'absolute');
8195             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8196             this.maskEl.right.hide();
8197             
8198             this.toolTip.hide();
8199             
8200             this.toolTip.el.hide();
8201             
8202             window.onwheel = function(){ return true;};
8203             
8204             if(this.intervalID){
8205                 window.clearInterval(this.intervalID);
8206                 this.intervalID = false;
8207             }
8208             
8209             this.isMasked = false;
8210             
8211         }
8212         
8213     }
8214     
8215 });
8216
8217 /*
8218  * Based on:
8219  * Ext JS Library 1.1.1
8220  * Copyright(c) 2006-2007, Ext JS, LLC.
8221  *
8222  * Originally Released Under LGPL - original licence link has changed is not relivant.
8223  *
8224  * Fork - LGPL
8225  * <script type="text/javascript">
8226  */
8227 /**
8228  * @class Roo.form.VTypes
8229  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8230  * @singleton
8231  */
8232 Roo.form.VTypes = function(){
8233     // closure these in so they are only created once.
8234     var alpha = /^[a-zA-Z_]+$/;
8235     var alphanum = /^[a-zA-Z0-9_]+$/;
8236     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8237     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8238
8239     // All these messages and functions are configurable
8240     return {
8241         /**
8242          * The function used to validate email addresses
8243          * @param {String} value The email address
8244          */
8245         'email' : function(v){
8246             return email.test(v);
8247         },
8248         /**
8249          * The error text to display when the email validation function returns false
8250          * @type String
8251          */
8252         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8253         /**
8254          * The keystroke filter mask to be applied on email input
8255          * @type RegExp
8256          */
8257         'emailMask' : /[a-z0-9_\.\-@]/i,
8258
8259         /**
8260          * The function used to validate URLs
8261          * @param {String} value The URL
8262          */
8263         'url' : function(v){
8264             return url.test(v);
8265         },
8266         /**
8267          * The error text to display when the url validation function returns false
8268          * @type String
8269          */
8270         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8271         
8272         /**
8273          * The function used to validate alpha values
8274          * @param {String} value The value
8275          */
8276         'alpha' : function(v){
8277             return alpha.test(v);
8278         },
8279         /**
8280          * The error text to display when the alpha validation function returns false
8281          * @type String
8282          */
8283         'alphaText' : 'This field should only contain letters and _',
8284         /**
8285          * The keystroke filter mask to be applied on alpha input
8286          * @type RegExp
8287          */
8288         'alphaMask' : /[a-z_]/i,
8289
8290         /**
8291          * The function used to validate alphanumeric values
8292          * @param {String} value The value
8293          */
8294         'alphanum' : function(v){
8295             return alphanum.test(v);
8296         },
8297         /**
8298          * The error text to display when the alphanumeric validation function returns false
8299          * @type String
8300          */
8301         'alphanumText' : 'This field should only contain letters, numbers and _',
8302         /**
8303          * The keystroke filter mask to be applied on alphanumeric input
8304          * @type RegExp
8305          */
8306         'alphanumMask' : /[a-z0-9_]/i
8307     };
8308 }();/*
8309  * - LGPL
8310  *
8311  * Input
8312  * 
8313  */
8314
8315 /**
8316  * @class Roo.bootstrap.Input
8317  * @extends Roo.bootstrap.Component
8318  * Bootstrap Input class
8319  * @cfg {Boolean} disabled is it disabled
8320  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8321  * @cfg {String} name name of the input
8322  * @cfg {string} fieldLabel - the label associated
8323  * @cfg {string} placeholder - placeholder to put in text.
8324  * @cfg {string}  before - input group add on before
8325  * @cfg {string} after - input group add on after
8326  * @cfg {string} size - (lg|sm) or leave empty..
8327  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8328  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8329  * @cfg {Number} md colspan out of 12 for computer-sized screens
8330  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8331  * @cfg {string} value default value of the input
8332  * @cfg {Number} labelWidth set the width of label 
8333  * @cfg {Number} labellg set the width of label (1-12)
8334  * @cfg {Number} labelmd set the width of label (1-12)
8335  * @cfg {Number} labelsm set the width of label (1-12)
8336  * @cfg {Number} labelxs set the width of label (1-12)
8337  * @cfg {String} labelAlign (top|left)
8338  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8339  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8340  * @cfg {String} indicatorpos (left|right) default left
8341
8342  * @cfg {String} align (left|center|right) Default left
8343  * @cfg {Boolean} forceFeedback (true|false) Default false
8344  * 
8345  * 
8346  * 
8347  * 
8348  * @constructor
8349  * Create a new Input
8350  * @param {Object} config The config object
8351  */
8352
8353 Roo.bootstrap.Input = function(config){
8354     
8355     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8356     
8357     this.addEvents({
8358         /**
8359          * @event focus
8360          * Fires when this field receives input focus.
8361          * @param {Roo.form.Field} this
8362          */
8363         focus : true,
8364         /**
8365          * @event blur
8366          * Fires when this field loses input focus.
8367          * @param {Roo.form.Field} this
8368          */
8369         blur : true,
8370         /**
8371          * @event specialkey
8372          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8373          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8374          * @param {Roo.form.Field} this
8375          * @param {Roo.EventObject} e The event object
8376          */
8377         specialkey : true,
8378         /**
8379          * @event change
8380          * Fires just before the field blurs if the field value has changed.
8381          * @param {Roo.form.Field} this
8382          * @param {Mixed} newValue The new value
8383          * @param {Mixed} oldValue The original value
8384          */
8385         change : true,
8386         /**
8387          * @event invalid
8388          * Fires after the field has been marked as invalid.
8389          * @param {Roo.form.Field} this
8390          * @param {String} msg The validation message
8391          */
8392         invalid : true,
8393         /**
8394          * @event valid
8395          * Fires after the field has been validated with no errors.
8396          * @param {Roo.form.Field} this
8397          */
8398         valid : true,
8399          /**
8400          * @event keyup
8401          * Fires after the key up
8402          * @param {Roo.form.Field} this
8403          * @param {Roo.EventObject}  e The event Object
8404          */
8405         keyup : true
8406     });
8407 };
8408
8409 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8410      /**
8411      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8412       automatic validation (defaults to "keyup").
8413      */
8414     validationEvent : "keyup",
8415      /**
8416      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8417      */
8418     validateOnBlur : true,
8419     /**
8420      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8421      */
8422     validationDelay : 250,
8423      /**
8424      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8425      */
8426     focusClass : "x-form-focus",  // not needed???
8427     
8428        
8429     /**
8430      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8431      */
8432     invalidClass : "has-warning",
8433     
8434     /**
8435      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8436      */
8437     validClass : "has-success",
8438     
8439     /**
8440      * @cfg {Boolean} hasFeedback (true|false) default true
8441      */
8442     hasFeedback : true,
8443     
8444     /**
8445      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8446      */
8447     invalidFeedbackClass : "glyphicon-warning-sign",
8448     
8449     /**
8450      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8451      */
8452     validFeedbackClass : "glyphicon-ok",
8453     
8454     /**
8455      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8456      */
8457     selectOnFocus : false,
8458     
8459      /**
8460      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8461      */
8462     maskRe : null,
8463        /**
8464      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8465      */
8466     vtype : null,
8467     
8468       /**
8469      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8470      */
8471     disableKeyFilter : false,
8472     
8473        /**
8474      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8475      */
8476     disabled : false,
8477      /**
8478      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8479      */
8480     allowBlank : true,
8481     /**
8482      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8483      */
8484     blankText : "Please complete this mandatory field",
8485     
8486      /**
8487      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8488      */
8489     minLength : 0,
8490     /**
8491      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8492      */
8493     maxLength : Number.MAX_VALUE,
8494     /**
8495      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8496      */
8497     minLengthText : "The minimum length for this field is {0}",
8498     /**
8499      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8500      */
8501     maxLengthText : "The maximum length for this field is {0}",
8502   
8503     
8504     /**
8505      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8506      * If available, this function will be called only after the basic validators all return true, and will be passed the
8507      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8508      */
8509     validator : null,
8510     /**
8511      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8512      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8513      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8514      */
8515     regex : null,
8516     /**
8517      * @cfg {String} regexText -- Depricated - use Invalid Text
8518      */
8519     regexText : "",
8520     
8521     /**
8522      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8523      */
8524     invalidText : "",
8525     
8526     
8527     
8528     autocomplete: false,
8529     
8530     
8531     fieldLabel : '',
8532     inputType : 'text',
8533     
8534     name : false,
8535     placeholder: false,
8536     before : false,
8537     after : false,
8538     size : false,
8539     hasFocus : false,
8540     preventMark: false,
8541     isFormField : true,
8542     value : '',
8543     labelWidth : 2,
8544     labelAlign : false,
8545     readOnly : false,
8546     align : false,
8547     formatedValue : false,
8548     forceFeedback : false,
8549     
8550     indicatorpos : 'left',
8551     
8552     labellg : 0,
8553     labelmd : 0,
8554     labelsm : 0,
8555     labelxs : 0,
8556     
8557     parentLabelAlign : function()
8558     {
8559         var parent = this;
8560         while (parent.parent()) {
8561             parent = parent.parent();
8562             if (typeof(parent.labelAlign) !='undefined') {
8563                 return parent.labelAlign;
8564             }
8565         }
8566         return 'left';
8567         
8568     },
8569     
8570     getAutoCreate : function()
8571     {
8572         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8573         
8574         var id = Roo.id();
8575         
8576         var cfg = {};
8577         
8578         if(this.inputType != 'hidden'){
8579             cfg.cls = 'form-group' //input-group
8580         }
8581         
8582         var input =  {
8583             tag: 'input',
8584             id : id,
8585             type : this.inputType,
8586             value : this.value,
8587             cls : 'form-control',
8588             placeholder : this.placeholder || '',
8589             autocomplete : this.autocomplete || 'new-password'
8590         };
8591         
8592         if(this.align){
8593             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8594         }
8595         
8596         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8597             input.maxLength = this.maxLength;
8598         }
8599         
8600         if (this.disabled) {
8601             input.disabled=true;
8602         }
8603         
8604         if (this.readOnly) {
8605             input.readonly=true;
8606         }
8607         
8608         if (this.name) {
8609             input.name = this.name;
8610         }
8611         
8612         if (this.size) {
8613             input.cls += ' input-' + this.size;
8614         }
8615         
8616         var settings=this;
8617         ['xs','sm','md','lg'].map(function(size){
8618             if (settings[size]) {
8619                 cfg.cls += ' col-' + size + '-' + settings[size];
8620             }
8621         });
8622         
8623         var inputblock = input;
8624         
8625         var feedback = {
8626             tag: 'span',
8627             cls: 'glyphicon form-control-feedback'
8628         };
8629             
8630         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8631             
8632             inputblock = {
8633                 cls : 'has-feedback',
8634                 cn :  [
8635                     input,
8636                     feedback
8637                 ] 
8638             };  
8639         }
8640         
8641         if (this.before || this.after) {
8642             
8643             inputblock = {
8644                 cls : 'input-group',
8645                 cn :  [] 
8646             };
8647             
8648             if (this.before && typeof(this.before) == 'string') {
8649                 
8650                 inputblock.cn.push({
8651                     tag :'span',
8652                     cls : 'roo-input-before input-group-addon',
8653                     html : this.before
8654                 });
8655             }
8656             if (this.before && typeof(this.before) == 'object') {
8657                 this.before = Roo.factory(this.before);
8658                 
8659                 inputblock.cn.push({
8660                     tag :'span',
8661                     cls : 'roo-input-before input-group-' +
8662                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8663                 });
8664             }
8665             
8666             inputblock.cn.push(input);
8667             
8668             if (this.after && typeof(this.after) == 'string') {
8669                 inputblock.cn.push({
8670                     tag :'span',
8671                     cls : 'roo-input-after input-group-addon',
8672                     html : this.after
8673                 });
8674             }
8675             if (this.after && typeof(this.after) == 'object') {
8676                 this.after = Roo.factory(this.after);
8677                 
8678                 inputblock.cn.push({
8679                     tag :'span',
8680                     cls : 'roo-input-after input-group-' +
8681                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8682                 });
8683             }
8684             
8685             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8686                 inputblock.cls += ' has-feedback';
8687                 inputblock.cn.push(feedback);
8688             }
8689         };
8690         
8691         if (align ==='left' && this.fieldLabel.length) {
8692             
8693             cfg.cls += ' roo-form-group-label-left';
8694             
8695             cfg.cn = [
8696                 {
8697                     tag : 'i',
8698                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8699                     tooltip : 'This field is required'
8700                 },
8701                 {
8702                     tag: 'label',
8703                     'for' :  id,
8704                     cls : 'control-label',
8705                     html : this.fieldLabel
8706
8707                 },
8708                 {
8709                     cls : "", 
8710                     cn: [
8711                         inputblock
8712                     ]
8713                 }
8714             ];
8715             
8716             var labelCfg = cfg.cn[1];
8717             var contentCfg = cfg.cn[2];
8718             
8719             if(this.indicatorpos == 'right'){
8720                 cfg.cn = [
8721                     {
8722                         tag: 'label',
8723                         'for' :  id,
8724                         cls : 'control-label',
8725                         html : this.fieldLabel
8726
8727                     },
8728                     {
8729                         tag : 'i',
8730                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8731                         tooltip : 'This field is required'
8732                     },
8733                     {
8734                         cls : "",
8735                         cn: [
8736                             inputblock
8737                         ]
8738                     }
8739
8740                 ];
8741                 
8742                 labelCfg = cfg.cn[0];
8743                 contentCfg = cfg.cn[2];
8744             
8745             }
8746             
8747             if(this.labelWidth > 12){
8748                 labelCfg.style = "width: " + this.labelWidth + 'px';
8749             }
8750             
8751             if(this.labelWidth < 13 && this.labelmd == 0){
8752                 this.labelmd = this.labelWidth;
8753             }
8754             
8755             if(this.labellg > 0){
8756                 labelCfg.cls += ' col-lg-' + this.labellg;
8757                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8758             }
8759             
8760             if(this.labelmd > 0){
8761                 labelCfg.cls += ' col-md-' + this.labelmd;
8762                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8763             }
8764             
8765             if(this.labelsm > 0){
8766                 labelCfg.cls += ' col-sm-' + this.labelsm;
8767                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8768             }
8769             
8770             if(this.labelxs > 0){
8771                 labelCfg.cls += ' col-xs-' + this.labelxs;
8772                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8773             }
8774             
8775             
8776         } else if ( this.fieldLabel.length) {
8777                 
8778             cfg.cn = [
8779                 {
8780                     tag : 'i',
8781                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8782                     tooltip : 'This field is required'
8783                 },
8784                 {
8785                     tag: 'label',
8786                    //cls : 'input-group-addon',
8787                     html : this.fieldLabel
8788
8789                 },
8790
8791                inputblock
8792
8793            ];
8794            
8795            if(this.indicatorpos == 'right'){
8796                 
8797                 cfg.cn = [
8798                     {
8799                         tag: 'label',
8800                        //cls : 'input-group-addon',
8801                         html : this.fieldLabel
8802
8803                     },
8804                     {
8805                         tag : 'i',
8806                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8807                         tooltip : 'This field is required'
8808                     },
8809
8810                    inputblock
8811
8812                ];
8813
8814             }
8815
8816         } else {
8817             
8818             cfg.cn = [
8819
8820                     inputblock
8821
8822             ];
8823                 
8824                 
8825         };
8826         
8827         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8828            cfg.cls += ' navbar-form';
8829         }
8830         
8831         if (this.parentType === 'NavGroup') {
8832            cfg.cls += ' navbar-form';
8833            cfg.tag = 'li';
8834         }
8835         
8836         return cfg;
8837         
8838     },
8839     /**
8840      * return the real input element.
8841      */
8842     inputEl: function ()
8843     {
8844         return this.el.select('input.form-control',true).first();
8845     },
8846     
8847     tooltipEl : function()
8848     {
8849         return this.inputEl();
8850     },
8851     
8852     indicatorEl : function()
8853     {
8854         var indicator = this.el.select('i.roo-required-indicator',true).first();
8855         
8856         if(!indicator){
8857             return false;
8858         }
8859         
8860         return indicator;
8861         
8862     },
8863     
8864     setDisabled : function(v)
8865     {
8866         var i  = this.inputEl().dom;
8867         if (!v) {
8868             i.removeAttribute('disabled');
8869             return;
8870             
8871         }
8872         i.setAttribute('disabled','true');
8873     },
8874     initEvents : function()
8875     {
8876           
8877         this.inputEl().on("keydown" , this.fireKey,  this);
8878         this.inputEl().on("focus", this.onFocus,  this);
8879         this.inputEl().on("blur", this.onBlur,  this);
8880         
8881         this.inputEl().relayEvent('keyup', this);
8882         
8883         this.indicator = this.indicatorEl();
8884         
8885         if(this.indicator){
8886             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8887             this.indicator.hide();
8888         }
8889  
8890         // reference to original value for reset
8891         this.originalValue = this.getValue();
8892         //Roo.form.TextField.superclass.initEvents.call(this);
8893         if(this.validationEvent == 'keyup'){
8894             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8895             this.inputEl().on('keyup', this.filterValidation, this);
8896         }
8897         else if(this.validationEvent !== false){
8898             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8899         }
8900         
8901         if(this.selectOnFocus){
8902             this.on("focus", this.preFocus, this);
8903             
8904         }
8905         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8906             this.inputEl().on("keypress", this.filterKeys, this);
8907         } else {
8908             this.inputEl().relayEvent('keypress', this);
8909         }
8910        /* if(this.grow){
8911             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8912             this.el.on("click", this.autoSize,  this);
8913         }
8914         */
8915         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8916             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8917         }
8918         
8919         if (typeof(this.before) == 'object') {
8920             this.before.render(this.el.select('.roo-input-before',true).first());
8921         }
8922         if (typeof(this.after) == 'object') {
8923             this.after.render(this.el.select('.roo-input-after',true).first());
8924         }
8925         
8926         
8927     },
8928     filterValidation : function(e){
8929         if(!e.isNavKeyPress()){
8930             this.validationTask.delay(this.validationDelay);
8931         }
8932     },
8933      /**
8934      * Validates the field value
8935      * @return {Boolean} True if the value is valid, else false
8936      */
8937     validate : function(){
8938         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8939         if(this.disabled || this.validateValue(this.getRawValue())){
8940             this.markValid();
8941             return true;
8942         }
8943         
8944         this.markInvalid();
8945         return false;
8946     },
8947     
8948     
8949     /**
8950      * Validates a value according to the field's validation rules and marks the field as invalid
8951      * if the validation fails
8952      * @param {Mixed} value The value to validate
8953      * @return {Boolean} True if the value is valid, else false
8954      */
8955     validateValue : function(value){
8956         if(value.length < 1)  { // if it's blank
8957             if(this.allowBlank){
8958                 return true;
8959             }            
8960             return this.inputEl().hasClass('hide') ? true : false;
8961         }
8962         
8963         if(value.length < this.minLength){
8964             return false;
8965         }
8966         if(value.length > this.maxLength){
8967             return false;
8968         }
8969         if(this.vtype){
8970             var vt = Roo.form.VTypes;
8971             if(!vt[this.vtype](value, this)){
8972                 return false;
8973             }
8974         }
8975         if(typeof this.validator == "function"){
8976             var msg = this.validator(value);
8977             if(msg !== true){
8978                 return false;
8979             }
8980             if (typeof(msg) == 'string') {
8981                 this.invalidText = msg;
8982             }
8983         }
8984         
8985         if(this.regex && !this.regex.test(value)){
8986             return false;
8987         }
8988         
8989         return true;
8990     },
8991
8992     
8993     
8994      // private
8995     fireKey : function(e){
8996         //Roo.log('field ' + e.getKey());
8997         if(e.isNavKeyPress()){
8998             this.fireEvent("specialkey", this, e);
8999         }
9000     },
9001     focus : function (selectText){
9002         if(this.rendered){
9003             this.inputEl().focus();
9004             if(selectText === true){
9005                 this.inputEl().dom.select();
9006             }
9007         }
9008         return this;
9009     } ,
9010     
9011     onFocus : function(){
9012         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9013            // this.el.addClass(this.focusClass);
9014         }
9015         if(!this.hasFocus){
9016             this.hasFocus = true;
9017             this.startValue = this.getValue();
9018             this.fireEvent("focus", this);
9019         }
9020     },
9021     
9022     beforeBlur : Roo.emptyFn,
9023
9024     
9025     // private
9026     onBlur : function(){
9027         this.beforeBlur();
9028         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9029             //this.el.removeClass(this.focusClass);
9030         }
9031         this.hasFocus = false;
9032         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9033             this.validate();
9034         }
9035         var v = this.getValue();
9036         if(String(v) !== String(this.startValue)){
9037             this.fireEvent('change', this, v, this.startValue);
9038         }
9039         this.fireEvent("blur", this);
9040     },
9041     
9042     /**
9043      * Resets the current field value to the originally loaded value and clears any validation messages
9044      */
9045     reset : function(){
9046         this.setValue(this.originalValue);
9047         this.validate();
9048     },
9049      /**
9050      * Returns the name of the field
9051      * @return {Mixed} name The name field
9052      */
9053     getName: function(){
9054         return this.name;
9055     },
9056      /**
9057      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9058      * @return {Mixed} value The field value
9059      */
9060     getValue : function(){
9061         
9062         var v = this.inputEl().getValue();
9063         
9064         return v;
9065     },
9066     /**
9067      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9068      * @return {Mixed} value The field value
9069      */
9070     getRawValue : function(){
9071         var v = this.inputEl().getValue();
9072         
9073         return v;
9074     },
9075     
9076     /**
9077      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9078      * @param {Mixed} value The value to set
9079      */
9080     setRawValue : function(v){
9081         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9082     },
9083     
9084     selectText : function(start, end){
9085         var v = this.getRawValue();
9086         if(v.length > 0){
9087             start = start === undefined ? 0 : start;
9088             end = end === undefined ? v.length : end;
9089             var d = this.inputEl().dom;
9090             if(d.setSelectionRange){
9091                 d.setSelectionRange(start, end);
9092             }else if(d.createTextRange){
9093                 var range = d.createTextRange();
9094                 range.moveStart("character", start);
9095                 range.moveEnd("character", v.length-end);
9096                 range.select();
9097             }
9098         }
9099     },
9100     
9101     /**
9102      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9103      * @param {Mixed} value The value to set
9104      */
9105     setValue : function(v){
9106         this.value = v;
9107         if(this.rendered){
9108             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9109             this.validate();
9110         }
9111     },
9112     
9113     /*
9114     processValue : function(value){
9115         if(this.stripCharsRe){
9116             var newValue = value.replace(this.stripCharsRe, '');
9117             if(newValue !== value){
9118                 this.setRawValue(newValue);
9119                 return newValue;
9120             }
9121         }
9122         return value;
9123     },
9124   */
9125     preFocus : function(){
9126         
9127         if(this.selectOnFocus){
9128             this.inputEl().dom.select();
9129         }
9130     },
9131     filterKeys : function(e){
9132         var k = e.getKey();
9133         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9134             return;
9135         }
9136         var c = e.getCharCode(), cc = String.fromCharCode(c);
9137         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9138             return;
9139         }
9140         if(!this.maskRe.test(cc)){
9141             e.stopEvent();
9142         }
9143     },
9144      /**
9145      * Clear any invalid styles/messages for this field
9146      */
9147     clearInvalid : function(){
9148         
9149         if(!this.el || this.preventMark){ // not rendered
9150             return;
9151         }
9152         
9153      
9154         this.el.removeClass(this.invalidClass);
9155         
9156         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9157             
9158             var feedback = this.el.select('.form-control-feedback', true).first();
9159             
9160             if(feedback){
9161                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9162             }
9163             
9164         }
9165         
9166         this.fireEvent('valid', this);
9167     },
9168     
9169      /**
9170      * Mark this field as valid
9171      */
9172     markValid : function()
9173     {
9174         if(!this.el  || this.preventMark){ // not rendered...
9175             return;
9176         }
9177         
9178         this.el.removeClass([this.invalidClass, this.validClass]);
9179         
9180         var feedback = this.el.select('.form-control-feedback', true).first();
9181             
9182         if(feedback){
9183             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9184         }
9185
9186         if(this.disabled){
9187             return;
9188         }
9189         
9190         if(this.allowBlank && !this.getRawValue().length){
9191             return;
9192         }
9193         
9194         if(this.indicator){
9195             this.indicator.hide();
9196         }
9197         
9198         this.el.addClass(this.validClass);
9199         
9200         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9201             
9202             var feedback = this.el.select('.form-control-feedback', true).first();
9203             
9204             if(feedback){
9205                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9206                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9207             }
9208             
9209         }
9210         
9211         this.fireEvent('valid', this);
9212     },
9213     
9214      /**
9215      * Mark this field as invalid
9216      * @param {String} msg The validation message
9217      */
9218     markInvalid : function(msg)
9219     {
9220         if(!this.el  || this.preventMark){ // not rendered
9221             return;
9222         }
9223         
9224         this.el.removeClass([this.invalidClass, this.validClass]);
9225         
9226         var feedback = this.el.select('.form-control-feedback', true).first();
9227             
9228         if(feedback){
9229             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9230         }
9231
9232         if(this.disabled){
9233             return;
9234         }
9235         
9236         if(this.allowBlank && !this.getRawValue().length){
9237             return;
9238         }
9239         
9240         if(this.indicator){
9241             this.indicator.show();
9242         }
9243         
9244         this.el.addClass(this.invalidClass);
9245         
9246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9247             
9248             var feedback = this.el.select('.form-control-feedback', true).first();
9249             
9250             if(feedback){
9251                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9252                 
9253                 if(this.getValue().length || this.forceFeedback){
9254                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9255                 }
9256                 
9257             }
9258             
9259         }
9260         
9261         this.fireEvent('invalid', this, msg);
9262     },
9263     // private
9264     SafariOnKeyDown : function(event)
9265     {
9266         // this is a workaround for a password hang bug on chrome/ webkit.
9267         if (this.inputEl().dom.type != 'password') {
9268             return;
9269         }
9270         
9271         var isSelectAll = false;
9272         
9273         if(this.inputEl().dom.selectionEnd > 0){
9274             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9275         }
9276         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9277             event.preventDefault();
9278             this.setValue('');
9279             return;
9280         }
9281         
9282         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9283             
9284             event.preventDefault();
9285             // this is very hacky as keydown always get's upper case.
9286             //
9287             var cc = String.fromCharCode(event.getCharCode());
9288             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9289             
9290         }
9291     },
9292     adjustWidth : function(tag, w){
9293         tag = tag.toLowerCase();
9294         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9295             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9296                 if(tag == 'input'){
9297                     return w + 2;
9298                 }
9299                 if(tag == 'textarea'){
9300                     return w-2;
9301                 }
9302             }else if(Roo.isOpera){
9303                 if(tag == 'input'){
9304                     return w + 2;
9305                 }
9306                 if(tag == 'textarea'){
9307                     return w-2;
9308                 }
9309             }
9310         }
9311         return w;
9312     }
9313     
9314 });
9315
9316  
9317 /*
9318  * - LGPL
9319  *
9320  * Input
9321  * 
9322  */
9323
9324 /**
9325  * @class Roo.bootstrap.TextArea
9326  * @extends Roo.bootstrap.Input
9327  * Bootstrap TextArea class
9328  * @cfg {Number} cols Specifies the visible width of a text area
9329  * @cfg {Number} rows Specifies the visible number of lines in a text area
9330  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9331  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9332  * @cfg {string} html text
9333  * 
9334  * @constructor
9335  * Create a new TextArea
9336  * @param {Object} config The config object
9337  */
9338
9339 Roo.bootstrap.TextArea = function(config){
9340     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9341    
9342 };
9343
9344 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9345      
9346     cols : false,
9347     rows : 5,
9348     readOnly : false,
9349     warp : 'soft',
9350     resize : false,
9351     value: false,
9352     html: false,
9353     
9354     getAutoCreate : function(){
9355         
9356         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9357         
9358         var id = Roo.id();
9359         
9360         var cfg = {};
9361         
9362         var input =  {
9363             tag: 'textarea',
9364             id : id,
9365             warp : this.warp,
9366             rows : this.rows,
9367             value : this.value || '',
9368             html: this.html || '',
9369             cls : 'form-control',
9370             placeholder : this.placeholder || '' 
9371             
9372         };
9373         
9374         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9375             input.maxLength = this.maxLength;
9376         }
9377         
9378         if(this.resize){
9379             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9380         }
9381         
9382         if(this.cols){
9383             input.cols = this.cols;
9384         }
9385         
9386         if (this.readOnly) {
9387             input.readonly = true;
9388         }
9389         
9390         if (this.name) {
9391             input.name = this.name;
9392         }
9393         
9394         if (this.size) {
9395             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9396         }
9397         
9398         var settings=this;
9399         ['xs','sm','md','lg'].map(function(size){
9400             if (settings[size]) {
9401                 cfg.cls += ' col-' + size + '-' + settings[size];
9402             }
9403         });
9404         
9405         var inputblock = input;
9406         
9407         if(this.hasFeedback && !this.allowBlank){
9408             
9409             var feedback = {
9410                 tag: 'span',
9411                 cls: 'glyphicon form-control-feedback'
9412             };
9413
9414             inputblock = {
9415                 cls : 'has-feedback',
9416                 cn :  [
9417                     input,
9418                     feedback
9419                 ] 
9420             };  
9421         }
9422         
9423         
9424         if (this.before || this.after) {
9425             
9426             inputblock = {
9427                 cls : 'input-group',
9428                 cn :  [] 
9429             };
9430             if (this.before) {
9431                 inputblock.cn.push({
9432                     tag :'span',
9433                     cls : 'input-group-addon',
9434                     html : this.before
9435                 });
9436             }
9437             
9438             inputblock.cn.push(input);
9439             
9440             if(this.hasFeedback && !this.allowBlank){
9441                 inputblock.cls += ' has-feedback';
9442                 inputblock.cn.push(feedback);
9443             }
9444             
9445             if (this.after) {
9446                 inputblock.cn.push({
9447                     tag :'span',
9448                     cls : 'input-group-addon',
9449                     html : this.after
9450                 });
9451             }
9452             
9453         }
9454         
9455         if (align ==='left' && this.fieldLabel.length) {
9456             cfg.cn = [
9457                 {
9458                     tag: 'label',
9459                     'for' :  id,
9460                     cls : 'control-label',
9461                     html : this.fieldLabel
9462                 },
9463                 {
9464                     cls : "",
9465                     cn: [
9466                         inputblock
9467                     ]
9468                 }
9469
9470             ];
9471             
9472             if(this.labelWidth > 12){
9473                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9474             }
9475
9476             if(this.labelWidth < 13 && this.labelmd == 0){
9477                 this.labelmd = this.labelWidth;
9478             }
9479
9480             if(this.labellg > 0){
9481                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9482                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9483             }
9484
9485             if(this.labelmd > 0){
9486                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9487                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9488             }
9489
9490             if(this.labelsm > 0){
9491                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9492                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9493             }
9494
9495             if(this.labelxs > 0){
9496                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9497                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9498             }
9499             
9500         } else if ( this.fieldLabel.length) {
9501             cfg.cn = [
9502
9503                {
9504                    tag: 'label',
9505                    //cls : 'input-group-addon',
9506                    html : this.fieldLabel
9507
9508                },
9509
9510                inputblock
9511
9512            ];
9513
9514         } else {
9515
9516             cfg.cn = [
9517
9518                 inputblock
9519
9520             ];
9521                 
9522         }
9523         
9524         if (this.disabled) {
9525             input.disabled=true;
9526         }
9527         
9528         return cfg;
9529         
9530     },
9531     /**
9532      * return the real textarea element.
9533      */
9534     inputEl: function ()
9535     {
9536         return this.el.select('textarea.form-control',true).first();
9537     },
9538     
9539     /**
9540      * Clear any invalid styles/messages for this field
9541      */
9542     clearInvalid : function()
9543     {
9544         
9545         if(!this.el || this.preventMark){ // not rendered
9546             return;
9547         }
9548         
9549         var label = this.el.select('label', true).first();
9550         var icon = this.el.select('i.fa-star', true).first();
9551         
9552         if(label && icon){
9553             icon.remove();
9554         }
9555         
9556         this.el.removeClass(this.invalidClass);
9557         
9558         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9559             
9560             var feedback = this.el.select('.form-control-feedback', true).first();
9561             
9562             if(feedback){
9563                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9564             }
9565             
9566         }
9567         
9568         this.fireEvent('valid', this);
9569     },
9570     
9571      /**
9572      * Mark this field as valid
9573      */
9574     markValid : function()
9575     {
9576         if(!this.el  || this.preventMark){ // not rendered
9577             return;
9578         }
9579         
9580         this.el.removeClass([this.invalidClass, this.validClass]);
9581         
9582         var feedback = this.el.select('.form-control-feedback', true).first();
9583             
9584         if(feedback){
9585             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9586         }
9587
9588         if(this.disabled || this.allowBlank){
9589             return;
9590         }
9591         
9592         var label = this.el.select('label', true).first();
9593         var icon = this.el.select('i.fa-star', true).first();
9594         
9595         if(label && icon){
9596             icon.remove();
9597         }
9598         
9599         this.el.addClass(this.validClass);
9600         
9601         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9602             
9603             var feedback = this.el.select('.form-control-feedback', true).first();
9604             
9605             if(feedback){
9606                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9607                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9608             }
9609             
9610         }
9611         
9612         this.fireEvent('valid', this);
9613     },
9614     
9615      /**
9616      * Mark this field as invalid
9617      * @param {String} msg The validation message
9618      */
9619     markInvalid : function(msg)
9620     {
9621         if(!this.el  || this.preventMark){ // not rendered
9622             return;
9623         }
9624         
9625         this.el.removeClass([this.invalidClass, this.validClass]);
9626         
9627         var feedback = this.el.select('.form-control-feedback', true).first();
9628             
9629         if(feedback){
9630             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9631         }
9632
9633         if(this.disabled || this.allowBlank){
9634             return;
9635         }
9636         
9637         var label = this.el.select('label', true).first();
9638         var icon = this.el.select('i.fa-star', true).first();
9639         
9640         if(!this.getValue().length && label && !icon){
9641             this.el.createChild({
9642                 tag : 'i',
9643                 cls : 'text-danger fa fa-lg fa-star',
9644                 tooltip : 'This field is required',
9645                 style : 'margin-right:5px;'
9646             }, label, true);
9647         }
9648
9649         this.el.addClass(this.invalidClass);
9650         
9651         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9652             
9653             var feedback = this.el.select('.form-control-feedback', true).first();
9654             
9655             if(feedback){
9656                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9657                 
9658                 if(this.getValue().length || this.forceFeedback){
9659                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9660                 }
9661                 
9662             }
9663             
9664         }
9665         
9666         this.fireEvent('invalid', this, msg);
9667     }
9668 });
9669
9670  
9671 /*
9672  * - LGPL
9673  *
9674  * trigger field - base class for combo..
9675  * 
9676  */
9677  
9678 /**
9679  * @class Roo.bootstrap.TriggerField
9680  * @extends Roo.bootstrap.Input
9681  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9682  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9683  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9684  * for which you can provide a custom implementation.  For example:
9685  * <pre><code>
9686 var trigger = new Roo.bootstrap.TriggerField();
9687 trigger.onTriggerClick = myTriggerFn;
9688 trigger.applyTo('my-field');
9689 </code></pre>
9690  *
9691  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9692  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9693  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9694  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9695  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9696
9697  * @constructor
9698  * Create a new TriggerField.
9699  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9700  * to the base TextField)
9701  */
9702 Roo.bootstrap.TriggerField = function(config){
9703     this.mimicing = false;
9704     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9705 };
9706
9707 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9708     /**
9709      * @cfg {String} triggerClass A CSS class to apply to the trigger
9710      */
9711      /**
9712      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9713      */
9714     hideTrigger:false,
9715
9716     /**
9717      * @cfg {Boolean} removable (true|false) special filter default false
9718      */
9719     removable : false,
9720     
9721     /** @cfg {Boolean} grow @hide */
9722     /** @cfg {Number} growMin @hide */
9723     /** @cfg {Number} growMax @hide */
9724
9725     /**
9726      * @hide 
9727      * @method
9728      */
9729     autoSize: Roo.emptyFn,
9730     // private
9731     monitorTab : true,
9732     // private
9733     deferHeight : true,
9734
9735     
9736     actionMode : 'wrap',
9737     
9738     caret : false,
9739     
9740     
9741     getAutoCreate : function(){
9742        
9743         var align = this.labelAlign || this.parentLabelAlign();
9744         
9745         var id = Roo.id();
9746         
9747         var cfg = {
9748             cls: 'form-group' //input-group
9749         };
9750         
9751         
9752         var input =  {
9753             tag: 'input',
9754             id : id,
9755             type : this.inputType,
9756             cls : 'form-control',
9757             autocomplete: 'new-password',
9758             placeholder : this.placeholder || '' 
9759             
9760         };
9761         if (this.name) {
9762             input.name = this.name;
9763         }
9764         if (this.size) {
9765             input.cls += ' input-' + this.size;
9766         }
9767         
9768         if (this.disabled) {
9769             input.disabled=true;
9770         }
9771         
9772         var inputblock = input;
9773         
9774         if(this.hasFeedback && !this.allowBlank){
9775             
9776             var feedback = {
9777                 tag: 'span',
9778                 cls: 'glyphicon form-control-feedback'
9779             };
9780             
9781             if(this.removable && !this.editable && !this.tickable){
9782                 inputblock = {
9783                     cls : 'has-feedback',
9784                     cn :  [
9785                         inputblock,
9786                         {
9787                             tag: 'button',
9788                             html : 'x',
9789                             cls : 'roo-combo-removable-btn close'
9790                         },
9791                         feedback
9792                     ] 
9793                 };
9794             } else {
9795                 inputblock = {
9796                     cls : 'has-feedback',
9797                     cn :  [
9798                         inputblock,
9799                         feedback
9800                     ] 
9801                 };
9802             }
9803
9804         } else {
9805             if(this.removable && !this.editable && !this.tickable){
9806                 inputblock = {
9807                     cls : 'roo-removable',
9808                     cn :  [
9809                         inputblock,
9810                         {
9811                             tag: 'button',
9812                             html : 'x',
9813                             cls : 'roo-combo-removable-btn close'
9814                         }
9815                     ] 
9816                 };
9817             }
9818         }
9819         
9820         if (this.before || this.after) {
9821             
9822             inputblock = {
9823                 cls : 'input-group',
9824                 cn :  [] 
9825             };
9826             if (this.before) {
9827                 inputblock.cn.push({
9828                     tag :'span',
9829                     cls : 'input-group-addon',
9830                     html : this.before
9831                 });
9832             }
9833             
9834             inputblock.cn.push(input);
9835             
9836             if(this.hasFeedback && !this.allowBlank){
9837                 inputblock.cls += ' has-feedback';
9838                 inputblock.cn.push(feedback);
9839             }
9840             
9841             if (this.after) {
9842                 inputblock.cn.push({
9843                     tag :'span',
9844                     cls : 'input-group-addon',
9845                     html : this.after
9846                 });
9847             }
9848             
9849         };
9850         
9851         var box = {
9852             tag: 'div',
9853             cn: [
9854                 {
9855                     tag: 'input',
9856                     type : 'hidden',
9857                     cls: 'form-hidden-field'
9858                 },
9859                 inputblock
9860             ]
9861             
9862         };
9863         
9864         if(this.multiple){
9865             box = {
9866                 tag: 'div',
9867                 cn: [
9868                     {
9869                         tag: 'input',
9870                         type : 'hidden',
9871                         cls: 'form-hidden-field'
9872                     },
9873                     {
9874                         tag: 'ul',
9875                         cls: 'roo-select2-choices',
9876                         cn:[
9877                             {
9878                                 tag: 'li',
9879                                 cls: 'roo-select2-search-field',
9880                                 cn: [
9881
9882                                     inputblock
9883                                 ]
9884                             }
9885                         ]
9886                     }
9887                 ]
9888             }
9889         };
9890         
9891         var combobox = {
9892             cls: 'roo-select2-container input-group',
9893             cn: [
9894                 box
9895 //                {
9896 //                    tag: 'ul',
9897 //                    cls: 'typeahead typeahead-long dropdown-menu',
9898 //                    style: 'display:none'
9899 //                }
9900             ]
9901         };
9902         
9903         if(!this.multiple && this.showToggleBtn){
9904             
9905             var caret = {
9906                         tag: 'span',
9907                         cls: 'caret'
9908              };
9909             if (this.caret != false) {
9910                 caret = {
9911                      tag: 'i',
9912                      cls: 'fa fa-' + this.caret
9913                 };
9914                 
9915             }
9916             
9917             combobox.cn.push({
9918                 tag :'span',
9919                 cls : 'input-group-addon btn dropdown-toggle',
9920                 cn : [
9921                     caret,
9922                     {
9923                         tag: 'span',
9924                         cls: 'combobox-clear',
9925                         cn  : [
9926                             {
9927                                 tag : 'i',
9928                                 cls: 'icon-remove'
9929                             }
9930                         ]
9931                     }
9932                 ]
9933
9934             })
9935         }
9936         
9937         if(this.multiple){
9938             combobox.cls += ' roo-select2-container-multi';
9939         }
9940         
9941         if (align ==='left' && this.fieldLabel.length) {
9942             
9943             cfg.cls += ' roo-form-group-label-left';
9944
9945             cfg.cn = [
9946                 {
9947                     tag : 'i',
9948                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9949                     tooltip : 'This field is required'
9950                 },
9951                 {
9952                     tag: 'label',
9953                     'for' :  id,
9954                     cls : 'control-label',
9955                     html : this.fieldLabel
9956
9957                 },
9958                 {
9959                     cls : "", 
9960                     cn: [
9961                         combobox
9962                     ]
9963                 }
9964
9965             ];
9966             
9967             var labelCfg = cfg.cn[1];
9968             var contentCfg = cfg.cn[2];
9969             
9970             if(this.indicatorpos == 'right'){
9971                 cfg.cn = [
9972                     {
9973                         tag: 'label',
9974                         'for' :  id,
9975                         cls : 'control-label',
9976                         cn : [
9977                             {
9978                                 tag : 'span',
9979                                 html : this.fieldLabel
9980                             },
9981                             {
9982                                 tag : 'i',
9983                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9984                                 tooltip : 'This field is required'
9985                             }
9986                         ]
9987                     },
9988                     {
9989                         cls : "", 
9990                         cn: [
9991                             combobox
9992                         ]
9993                     }
9994
9995                 ];
9996                 
9997                 labelCfg = cfg.cn[0];
9998                 contentCfg = cfg.cn[1];
9999             }
10000             
10001             if(this.labelWidth > 12){
10002                 labelCfg.style = "width: " + this.labelWidth + 'px';
10003             }
10004             
10005             if(this.labelWidth < 13 && this.labelmd == 0){
10006                 this.labelmd = this.labelWidth;
10007             }
10008             
10009             if(this.labellg > 0){
10010                 labelCfg.cls += ' col-lg-' + this.labellg;
10011                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10012             }
10013             
10014             if(this.labelmd > 0){
10015                 labelCfg.cls += ' col-md-' + this.labelmd;
10016                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10017             }
10018             
10019             if(this.labelsm > 0){
10020                 labelCfg.cls += ' col-sm-' + this.labelsm;
10021                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10022             }
10023             
10024             if(this.labelxs > 0){
10025                 labelCfg.cls += ' col-xs-' + this.labelxs;
10026                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10027             }
10028             
10029         } else if ( this.fieldLabel.length) {
10030 //                Roo.log(" label");
10031             cfg.cn = [
10032                 {
10033                    tag : 'i',
10034                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10035                    tooltip : 'This field is required'
10036                },
10037                {
10038                    tag: 'label',
10039                    //cls : 'input-group-addon',
10040                    html : this.fieldLabel
10041
10042                },
10043
10044                combobox
10045
10046             ];
10047             
10048             if(this.indicatorpos == 'right'){
10049                 
10050                 cfg.cn = [
10051                     {
10052                        tag: 'label',
10053                        cn : [
10054                            {
10055                                tag : 'span',
10056                                html : this.fieldLabel
10057                            },
10058                            {
10059                               tag : 'i',
10060                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10061                               tooltip : 'This field is required'
10062                            }
10063                        ]
10064
10065                     },
10066                     combobox
10067
10068                 ];
10069
10070             }
10071
10072         } else {
10073             
10074 //                Roo.log(" no label && no align");
10075                 cfg = combobox
10076                      
10077                 
10078         }
10079         
10080         var settings=this;
10081         ['xs','sm','md','lg'].map(function(size){
10082             if (settings[size]) {
10083                 cfg.cls += ' col-' + size + '-' + settings[size];
10084             }
10085         });
10086         
10087         return cfg;
10088         
10089     },
10090     
10091     
10092     
10093     // private
10094     onResize : function(w, h){
10095 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10096 //        if(typeof w == 'number'){
10097 //            var x = w - this.trigger.getWidth();
10098 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10099 //            this.trigger.setStyle('left', x+'px');
10100 //        }
10101     },
10102
10103     // private
10104     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10105
10106     // private
10107     getResizeEl : function(){
10108         return this.inputEl();
10109     },
10110
10111     // private
10112     getPositionEl : function(){
10113         return this.inputEl();
10114     },
10115
10116     // private
10117     alignErrorIcon : function(){
10118         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10119     },
10120
10121     // private
10122     initEvents : function(){
10123         
10124         this.createList();
10125         
10126         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10127         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10128         if(!this.multiple && this.showToggleBtn){
10129             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10130             if(this.hideTrigger){
10131                 this.trigger.setDisplayed(false);
10132             }
10133             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10134         }
10135         
10136         if(this.multiple){
10137             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10138         }
10139         
10140         if(this.removable && !this.editable && !this.tickable){
10141             var close = this.closeTriggerEl();
10142             
10143             if(close){
10144                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10145                 close.on('click', this.removeBtnClick, this, close);
10146             }
10147         }
10148         
10149         //this.trigger.addClassOnOver('x-form-trigger-over');
10150         //this.trigger.addClassOnClick('x-form-trigger-click');
10151         
10152         //if(!this.width){
10153         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10154         //}
10155     },
10156     
10157     closeTriggerEl : function()
10158     {
10159         var close = this.el.select('.roo-combo-removable-btn', true).first();
10160         return close ? close : false;
10161     },
10162     
10163     removeBtnClick : function(e, h, el)
10164     {
10165         e.preventDefault();
10166         
10167         if(this.fireEvent("remove", this) !== false){
10168             this.reset();
10169             this.fireEvent("afterremove", this)
10170         }
10171     },
10172     
10173     createList : function()
10174     {
10175         this.list = Roo.get(document.body).createChild({
10176             tag: 'ul',
10177             cls: 'typeahead typeahead-long dropdown-menu',
10178             style: 'display:none'
10179         });
10180         
10181         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10182         
10183     },
10184
10185     // private
10186     initTrigger : function(){
10187        
10188     },
10189
10190     // private
10191     onDestroy : function(){
10192         if(this.trigger){
10193             this.trigger.removeAllListeners();
10194           //  this.trigger.remove();
10195         }
10196         //if(this.wrap){
10197         //    this.wrap.remove();
10198         //}
10199         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10200     },
10201
10202     // private
10203     onFocus : function(){
10204         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10205         /*
10206         if(!this.mimicing){
10207             this.wrap.addClass('x-trigger-wrap-focus');
10208             this.mimicing = true;
10209             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10210             if(this.monitorTab){
10211                 this.el.on("keydown", this.checkTab, this);
10212             }
10213         }
10214         */
10215     },
10216
10217     // private
10218     checkTab : function(e){
10219         if(e.getKey() == e.TAB){
10220             this.triggerBlur();
10221         }
10222     },
10223
10224     // private
10225     onBlur : function(){
10226         // do nothing
10227     },
10228
10229     // private
10230     mimicBlur : function(e, t){
10231         /*
10232         if(!this.wrap.contains(t) && this.validateBlur()){
10233             this.triggerBlur();
10234         }
10235         */
10236     },
10237
10238     // private
10239     triggerBlur : function(){
10240         this.mimicing = false;
10241         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10242         if(this.monitorTab){
10243             this.el.un("keydown", this.checkTab, this);
10244         }
10245         //this.wrap.removeClass('x-trigger-wrap-focus');
10246         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10247     },
10248
10249     // private
10250     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10251     validateBlur : function(e, t){
10252         return true;
10253     },
10254
10255     // private
10256     onDisable : function(){
10257         this.inputEl().dom.disabled = true;
10258         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10259         //if(this.wrap){
10260         //    this.wrap.addClass('x-item-disabled');
10261         //}
10262     },
10263
10264     // private
10265     onEnable : function(){
10266         this.inputEl().dom.disabled = false;
10267         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10268         //if(this.wrap){
10269         //    this.el.removeClass('x-item-disabled');
10270         //}
10271     },
10272
10273     // private
10274     onShow : function(){
10275         var ae = this.getActionEl();
10276         
10277         if(ae){
10278             ae.dom.style.display = '';
10279             ae.dom.style.visibility = 'visible';
10280         }
10281     },
10282
10283     // private
10284     
10285     onHide : function(){
10286         var ae = this.getActionEl();
10287         ae.dom.style.display = 'none';
10288     },
10289
10290     /**
10291      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10292      * by an implementing function.
10293      * @method
10294      * @param {EventObject} e
10295      */
10296     onTriggerClick : Roo.emptyFn
10297 });
10298  /*
10299  * Based on:
10300  * Ext JS Library 1.1.1
10301  * Copyright(c) 2006-2007, Ext JS, LLC.
10302  *
10303  * Originally Released Under LGPL - original licence link has changed is not relivant.
10304  *
10305  * Fork - LGPL
10306  * <script type="text/javascript">
10307  */
10308
10309
10310 /**
10311  * @class Roo.data.SortTypes
10312  * @singleton
10313  * Defines the default sorting (casting?) comparison functions used when sorting data.
10314  */
10315 Roo.data.SortTypes = {
10316     /**
10317      * Default sort that does nothing
10318      * @param {Mixed} s The value being converted
10319      * @return {Mixed} The comparison value
10320      */
10321     none : function(s){
10322         return s;
10323     },
10324     
10325     /**
10326      * The regular expression used to strip tags
10327      * @type {RegExp}
10328      * @property
10329      */
10330     stripTagsRE : /<\/?[^>]+>/gi,
10331     
10332     /**
10333      * Strips all HTML tags to sort on text only
10334      * @param {Mixed} s The value being converted
10335      * @return {String} The comparison value
10336      */
10337     asText : function(s){
10338         return String(s).replace(this.stripTagsRE, "");
10339     },
10340     
10341     /**
10342      * Strips all HTML tags to sort on text only - Case insensitive
10343      * @param {Mixed} s The value being converted
10344      * @return {String} The comparison value
10345      */
10346     asUCText : function(s){
10347         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10348     },
10349     
10350     /**
10351      * Case insensitive string
10352      * @param {Mixed} s The value being converted
10353      * @return {String} The comparison value
10354      */
10355     asUCString : function(s) {
10356         return String(s).toUpperCase();
10357     },
10358     
10359     /**
10360      * Date sorting
10361      * @param {Mixed} s The value being converted
10362      * @return {Number} The comparison value
10363      */
10364     asDate : function(s) {
10365         if(!s){
10366             return 0;
10367         }
10368         if(s instanceof Date){
10369             return s.getTime();
10370         }
10371         return Date.parse(String(s));
10372     },
10373     
10374     /**
10375      * Float sorting
10376      * @param {Mixed} s The value being converted
10377      * @return {Float} The comparison value
10378      */
10379     asFloat : function(s) {
10380         var val = parseFloat(String(s).replace(/,/g, ""));
10381         if(isNaN(val)) {
10382             val = 0;
10383         }
10384         return val;
10385     },
10386     
10387     /**
10388      * Integer sorting
10389      * @param {Mixed} s The value being converted
10390      * @return {Number} The comparison value
10391      */
10392     asInt : function(s) {
10393         var val = parseInt(String(s).replace(/,/g, ""));
10394         if(isNaN(val)) {
10395             val = 0;
10396         }
10397         return val;
10398     }
10399 };/*
10400  * Based on:
10401  * Ext JS Library 1.1.1
10402  * Copyright(c) 2006-2007, Ext JS, LLC.
10403  *
10404  * Originally Released Under LGPL - original licence link has changed is not relivant.
10405  *
10406  * Fork - LGPL
10407  * <script type="text/javascript">
10408  */
10409
10410 /**
10411 * @class Roo.data.Record
10412  * Instances of this class encapsulate both record <em>definition</em> information, and record
10413  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10414  * to access Records cached in an {@link Roo.data.Store} object.<br>
10415  * <p>
10416  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10417  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10418  * objects.<br>
10419  * <p>
10420  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10421  * @constructor
10422  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10423  * {@link #create}. The parameters are the same.
10424  * @param {Array} data An associative Array of data values keyed by the field name.
10425  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10426  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10427  * not specified an integer id is generated.
10428  */
10429 Roo.data.Record = function(data, id){
10430     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10431     this.data = data;
10432 };
10433
10434 /**
10435  * Generate a constructor for a specific record layout.
10436  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10437  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10438  * Each field definition object may contain the following properties: <ul>
10439  * <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,
10440  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10441  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10442  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10443  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10444  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10445  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10446  * this may be omitted.</p></li>
10447  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10448  * <ul><li>auto (Default, implies no conversion)</li>
10449  * <li>string</li>
10450  * <li>int</li>
10451  * <li>float</li>
10452  * <li>boolean</li>
10453  * <li>date</li></ul></p></li>
10454  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10455  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10456  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10457  * by the Reader into an object that will be stored in the Record. It is passed the
10458  * following parameters:<ul>
10459  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10460  * </ul></p></li>
10461  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10462  * </ul>
10463  * <br>usage:<br><pre><code>
10464 var TopicRecord = Roo.data.Record.create(
10465     {name: 'title', mapping: 'topic_title'},
10466     {name: 'author', mapping: 'username'},
10467     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10468     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10469     {name: 'lastPoster', mapping: 'user2'},
10470     {name: 'excerpt', mapping: 'post_text'}
10471 );
10472
10473 var myNewRecord = new TopicRecord({
10474     title: 'Do my job please',
10475     author: 'noobie',
10476     totalPosts: 1,
10477     lastPost: new Date(),
10478     lastPoster: 'Animal',
10479     excerpt: 'No way dude!'
10480 });
10481 myStore.add(myNewRecord);
10482 </code></pre>
10483  * @method create
10484  * @static
10485  */
10486 Roo.data.Record.create = function(o){
10487     var f = function(){
10488         f.superclass.constructor.apply(this, arguments);
10489     };
10490     Roo.extend(f, Roo.data.Record);
10491     var p = f.prototype;
10492     p.fields = new Roo.util.MixedCollection(false, function(field){
10493         return field.name;
10494     });
10495     for(var i = 0, len = o.length; i < len; i++){
10496         p.fields.add(new Roo.data.Field(o[i]));
10497     }
10498     f.getField = function(name){
10499         return p.fields.get(name);  
10500     };
10501     return f;
10502 };
10503
10504 Roo.data.Record.AUTO_ID = 1000;
10505 Roo.data.Record.EDIT = 'edit';
10506 Roo.data.Record.REJECT = 'reject';
10507 Roo.data.Record.COMMIT = 'commit';
10508
10509 Roo.data.Record.prototype = {
10510     /**
10511      * Readonly flag - true if this record has been modified.
10512      * @type Boolean
10513      */
10514     dirty : false,
10515     editing : false,
10516     error: null,
10517     modified: null,
10518
10519     // private
10520     join : function(store){
10521         this.store = store;
10522     },
10523
10524     /**
10525      * Set the named field to the specified value.
10526      * @param {String} name The name of the field to set.
10527      * @param {Object} value The value to set the field to.
10528      */
10529     set : function(name, value){
10530         if(this.data[name] == value){
10531             return;
10532         }
10533         this.dirty = true;
10534         if(!this.modified){
10535             this.modified = {};
10536         }
10537         if(typeof this.modified[name] == 'undefined'){
10538             this.modified[name] = this.data[name];
10539         }
10540         this.data[name] = value;
10541         if(!this.editing && this.store){
10542             this.store.afterEdit(this);
10543         }       
10544     },
10545
10546     /**
10547      * Get the value of the named field.
10548      * @param {String} name The name of the field to get the value of.
10549      * @return {Object} The value of the field.
10550      */
10551     get : function(name){
10552         return this.data[name]; 
10553     },
10554
10555     // private
10556     beginEdit : function(){
10557         this.editing = true;
10558         this.modified = {}; 
10559     },
10560
10561     // private
10562     cancelEdit : function(){
10563         this.editing = false;
10564         delete this.modified;
10565     },
10566
10567     // private
10568     endEdit : function(){
10569         this.editing = false;
10570         if(this.dirty && this.store){
10571             this.store.afterEdit(this);
10572         }
10573     },
10574
10575     /**
10576      * Usually called by the {@link Roo.data.Store} which owns the Record.
10577      * Rejects all changes made to the Record since either creation, or the last commit operation.
10578      * Modified fields are reverted to their original values.
10579      * <p>
10580      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10581      * of reject operations.
10582      */
10583     reject : function(){
10584         var m = this.modified;
10585         for(var n in m){
10586             if(typeof m[n] != "function"){
10587                 this.data[n] = m[n];
10588             }
10589         }
10590         this.dirty = false;
10591         delete this.modified;
10592         this.editing = false;
10593         if(this.store){
10594             this.store.afterReject(this);
10595         }
10596     },
10597
10598     /**
10599      * Usually called by the {@link Roo.data.Store} which owns the Record.
10600      * Commits all changes made to the Record since either creation, or the last commit operation.
10601      * <p>
10602      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10603      * of commit operations.
10604      */
10605     commit : function(){
10606         this.dirty = false;
10607         delete this.modified;
10608         this.editing = false;
10609         if(this.store){
10610             this.store.afterCommit(this);
10611         }
10612     },
10613
10614     // private
10615     hasError : function(){
10616         return this.error != null;
10617     },
10618
10619     // private
10620     clearError : function(){
10621         this.error = null;
10622     },
10623
10624     /**
10625      * Creates a copy of this record.
10626      * @param {String} id (optional) A new record id if you don't want to use this record's id
10627      * @return {Record}
10628      */
10629     copy : function(newId) {
10630         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10631     }
10632 };/*
10633  * Based on:
10634  * Ext JS Library 1.1.1
10635  * Copyright(c) 2006-2007, Ext JS, LLC.
10636  *
10637  * Originally Released Under LGPL - original licence link has changed is not relivant.
10638  *
10639  * Fork - LGPL
10640  * <script type="text/javascript">
10641  */
10642
10643
10644
10645 /**
10646  * @class Roo.data.Store
10647  * @extends Roo.util.Observable
10648  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10649  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10650  * <p>
10651  * 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
10652  * has no knowledge of the format of the data returned by the Proxy.<br>
10653  * <p>
10654  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10655  * instances from the data object. These records are cached and made available through accessor functions.
10656  * @constructor
10657  * Creates a new Store.
10658  * @param {Object} config A config object containing the objects needed for the Store to access data,
10659  * and read the data into Records.
10660  */
10661 Roo.data.Store = function(config){
10662     this.data = new Roo.util.MixedCollection(false);
10663     this.data.getKey = function(o){
10664         return o.id;
10665     };
10666     this.baseParams = {};
10667     // private
10668     this.paramNames = {
10669         "start" : "start",
10670         "limit" : "limit",
10671         "sort" : "sort",
10672         "dir" : "dir",
10673         "multisort" : "_multisort"
10674     };
10675
10676     if(config && config.data){
10677         this.inlineData = config.data;
10678         delete config.data;
10679     }
10680
10681     Roo.apply(this, config);
10682     
10683     if(this.reader){ // reader passed
10684         this.reader = Roo.factory(this.reader, Roo.data);
10685         this.reader.xmodule = this.xmodule || false;
10686         if(!this.recordType){
10687             this.recordType = this.reader.recordType;
10688         }
10689         if(this.reader.onMetaChange){
10690             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10691         }
10692     }
10693
10694     if(this.recordType){
10695         this.fields = this.recordType.prototype.fields;
10696     }
10697     this.modified = [];
10698
10699     this.addEvents({
10700         /**
10701          * @event datachanged
10702          * Fires when the data cache has changed, and a widget which is using this Store
10703          * as a Record cache should refresh its view.
10704          * @param {Store} this
10705          */
10706         datachanged : true,
10707         /**
10708          * @event metachange
10709          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10710          * @param {Store} this
10711          * @param {Object} meta The JSON metadata
10712          */
10713         metachange : true,
10714         /**
10715          * @event add
10716          * Fires when Records have been added to the Store
10717          * @param {Store} this
10718          * @param {Roo.data.Record[]} records The array of Records added
10719          * @param {Number} index The index at which the record(s) were added
10720          */
10721         add : true,
10722         /**
10723          * @event remove
10724          * Fires when a Record has been removed from the Store
10725          * @param {Store} this
10726          * @param {Roo.data.Record} record The Record that was removed
10727          * @param {Number} index The index at which the record was removed
10728          */
10729         remove : true,
10730         /**
10731          * @event update
10732          * Fires when a Record has been updated
10733          * @param {Store} this
10734          * @param {Roo.data.Record} record The Record that was updated
10735          * @param {String} operation The update operation being performed.  Value may be one of:
10736          * <pre><code>
10737  Roo.data.Record.EDIT
10738  Roo.data.Record.REJECT
10739  Roo.data.Record.COMMIT
10740          * </code></pre>
10741          */
10742         update : true,
10743         /**
10744          * @event clear
10745          * Fires when the data cache has been cleared.
10746          * @param {Store} this
10747          */
10748         clear : true,
10749         /**
10750          * @event beforeload
10751          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10752          * the load action will be canceled.
10753          * @param {Store} this
10754          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10755          */
10756         beforeload : true,
10757         /**
10758          * @event beforeloadadd
10759          * Fires after a new set of Records has been loaded.
10760          * @param {Store} this
10761          * @param {Roo.data.Record[]} records The Records that were loaded
10762          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10763          */
10764         beforeloadadd : true,
10765         /**
10766          * @event load
10767          * Fires after a new set of Records has been loaded, before they are added to the store.
10768          * @param {Store} this
10769          * @param {Roo.data.Record[]} records The Records that were loaded
10770          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10771          * @params {Object} return from reader
10772          */
10773         load : true,
10774         /**
10775          * @event loadexception
10776          * Fires if an exception occurs in the Proxy during loading.
10777          * Called with the signature of the Proxy's "loadexception" event.
10778          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10779          * 
10780          * @param {Proxy} 
10781          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10782          * @param {Object} load options 
10783          * @param {Object} jsonData from your request (normally this contains the Exception)
10784          */
10785         loadexception : true
10786     });
10787     
10788     if(this.proxy){
10789         this.proxy = Roo.factory(this.proxy, Roo.data);
10790         this.proxy.xmodule = this.xmodule || false;
10791         this.relayEvents(this.proxy,  ["loadexception"]);
10792     }
10793     this.sortToggle = {};
10794     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10795
10796     Roo.data.Store.superclass.constructor.call(this);
10797
10798     if(this.inlineData){
10799         this.loadData(this.inlineData);
10800         delete this.inlineData;
10801     }
10802 };
10803
10804 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10805      /**
10806     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10807     * without a remote query - used by combo/forms at present.
10808     */
10809     
10810     /**
10811     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10812     */
10813     /**
10814     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10815     */
10816     /**
10817     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10818     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10819     */
10820     /**
10821     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10822     * on any HTTP request
10823     */
10824     /**
10825     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10826     */
10827     /**
10828     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10829     */
10830     multiSort: false,
10831     /**
10832     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10833     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10834     */
10835     remoteSort : false,
10836
10837     /**
10838     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10839      * loaded or when a record is removed. (defaults to false).
10840     */
10841     pruneModifiedRecords : false,
10842
10843     // private
10844     lastOptions : null,
10845
10846     /**
10847      * Add Records to the Store and fires the add event.
10848      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10849      */
10850     add : function(records){
10851         records = [].concat(records);
10852         for(var i = 0, len = records.length; i < len; i++){
10853             records[i].join(this);
10854         }
10855         var index = this.data.length;
10856         this.data.addAll(records);
10857         this.fireEvent("add", this, records, index);
10858     },
10859
10860     /**
10861      * Remove a Record from the Store and fires the remove event.
10862      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10863      */
10864     remove : function(record){
10865         var index = this.data.indexOf(record);
10866         this.data.removeAt(index);
10867         if(this.pruneModifiedRecords){
10868             this.modified.remove(record);
10869         }
10870         this.fireEvent("remove", this, record, index);
10871     },
10872
10873     /**
10874      * Remove all Records from the Store and fires the clear event.
10875      */
10876     removeAll : function(){
10877         this.data.clear();
10878         if(this.pruneModifiedRecords){
10879             this.modified = [];
10880         }
10881         this.fireEvent("clear", this);
10882     },
10883
10884     /**
10885      * Inserts Records to the Store at the given index and fires the add event.
10886      * @param {Number} index The start index at which to insert the passed Records.
10887      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10888      */
10889     insert : function(index, records){
10890         records = [].concat(records);
10891         for(var i = 0, len = records.length; i < len; i++){
10892             this.data.insert(index, records[i]);
10893             records[i].join(this);
10894         }
10895         this.fireEvent("add", this, records, index);
10896     },
10897
10898     /**
10899      * Get the index within the cache of the passed Record.
10900      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10901      * @return {Number} The index of the passed Record. Returns -1 if not found.
10902      */
10903     indexOf : function(record){
10904         return this.data.indexOf(record);
10905     },
10906
10907     /**
10908      * Get the index within the cache of the Record with the passed id.
10909      * @param {String} id The id of the Record to find.
10910      * @return {Number} The index of the Record. Returns -1 if not found.
10911      */
10912     indexOfId : function(id){
10913         return this.data.indexOfKey(id);
10914     },
10915
10916     /**
10917      * Get the Record with the specified id.
10918      * @param {String} id The id of the Record to find.
10919      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10920      */
10921     getById : function(id){
10922         return this.data.key(id);
10923     },
10924
10925     /**
10926      * Get the Record at the specified index.
10927      * @param {Number} index The index of the Record to find.
10928      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10929      */
10930     getAt : function(index){
10931         return this.data.itemAt(index);
10932     },
10933
10934     /**
10935      * Returns a range of Records between specified indices.
10936      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10937      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10938      * @return {Roo.data.Record[]} An array of Records
10939      */
10940     getRange : function(start, end){
10941         return this.data.getRange(start, end);
10942     },
10943
10944     // private
10945     storeOptions : function(o){
10946         o = Roo.apply({}, o);
10947         delete o.callback;
10948         delete o.scope;
10949         this.lastOptions = o;
10950     },
10951
10952     /**
10953      * Loads the Record cache from the configured Proxy using the configured Reader.
10954      * <p>
10955      * If using remote paging, then the first load call must specify the <em>start</em>
10956      * and <em>limit</em> properties in the options.params property to establish the initial
10957      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10958      * <p>
10959      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10960      * and this call will return before the new data has been loaded. Perform any post-processing
10961      * in a callback function, or in a "load" event handler.</strong>
10962      * <p>
10963      * @param {Object} options An object containing properties which control loading options:<ul>
10964      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10965      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10966      * passed the following arguments:<ul>
10967      * <li>r : Roo.data.Record[]</li>
10968      * <li>options: Options object from the load call</li>
10969      * <li>success: Boolean success indicator</li></ul></li>
10970      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10971      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10972      * </ul>
10973      */
10974     load : function(options){
10975         options = options || {};
10976         if(this.fireEvent("beforeload", this, options) !== false){
10977             this.storeOptions(options);
10978             var p = Roo.apply(options.params || {}, this.baseParams);
10979             // if meta was not loaded from remote source.. try requesting it.
10980             if (!this.reader.metaFromRemote) {
10981                 p._requestMeta = 1;
10982             }
10983             if(this.sortInfo && this.remoteSort){
10984                 var pn = this.paramNames;
10985                 p[pn["sort"]] = this.sortInfo.field;
10986                 p[pn["dir"]] = this.sortInfo.direction;
10987             }
10988             if (this.multiSort) {
10989                 var pn = this.paramNames;
10990                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10991             }
10992             
10993             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10994         }
10995     },
10996
10997     /**
10998      * Reloads the Record cache from the configured Proxy using the configured Reader and
10999      * the options from the last load operation performed.
11000      * @param {Object} options (optional) An object containing properties which may override the options
11001      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11002      * the most recently used options are reused).
11003      */
11004     reload : function(options){
11005         this.load(Roo.applyIf(options||{}, this.lastOptions));
11006     },
11007
11008     // private
11009     // Called as a callback by the Reader during a load operation.
11010     loadRecords : function(o, options, success){
11011         if(!o || success === false){
11012             if(success !== false){
11013                 this.fireEvent("load", this, [], options, o);
11014             }
11015             if(options.callback){
11016                 options.callback.call(options.scope || this, [], options, false);
11017             }
11018             return;
11019         }
11020         // if data returned failure - throw an exception.
11021         if (o.success === false) {
11022             // show a message if no listener is registered.
11023             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11024                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11025             }
11026             // loadmask wil be hooked into this..
11027             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11028             return;
11029         }
11030         var r = o.records, t = o.totalRecords || r.length;
11031         
11032         this.fireEvent("beforeloadadd", this, r, options, o);
11033         
11034         if(!options || options.add !== true){
11035             if(this.pruneModifiedRecords){
11036                 this.modified = [];
11037             }
11038             for(var i = 0, len = r.length; i < len; i++){
11039                 r[i].join(this);
11040             }
11041             if(this.snapshot){
11042                 this.data = this.snapshot;
11043                 delete this.snapshot;
11044             }
11045             this.data.clear();
11046             this.data.addAll(r);
11047             this.totalLength = t;
11048             this.applySort();
11049             this.fireEvent("datachanged", this);
11050         }else{
11051             this.totalLength = Math.max(t, this.data.length+r.length);
11052             this.add(r);
11053         }
11054         
11055         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11056                 
11057             var e = new Roo.data.Record({});
11058
11059             e.set(this.parent.displayField, this.parent.emptyTitle);
11060             e.set(this.parent.valueField, '');
11061
11062             this.insert(0, e);
11063         }
11064             
11065         this.fireEvent("load", this, r, options, o);
11066         if(options.callback){
11067             options.callback.call(options.scope || this, r, options, true);
11068         }
11069     },
11070
11071
11072     /**
11073      * Loads data from a passed data block. A Reader which understands the format of the data
11074      * must have been configured in the constructor.
11075      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11076      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11077      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11078      */
11079     loadData : function(o, append){
11080         var r = this.reader.readRecords(o);
11081         this.loadRecords(r, {add: append}, true);
11082     },
11083
11084     /**
11085      * Gets the number of cached records.
11086      * <p>
11087      * <em>If using paging, this may not be the total size of the dataset. If the data object
11088      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11089      * the data set size</em>
11090      */
11091     getCount : function(){
11092         return this.data.length || 0;
11093     },
11094
11095     /**
11096      * Gets the total number of records in the dataset as returned by the server.
11097      * <p>
11098      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11099      * the dataset size</em>
11100      */
11101     getTotalCount : function(){
11102         return this.totalLength || 0;
11103     },
11104
11105     /**
11106      * Returns the sort state of the Store as an object with two properties:
11107      * <pre><code>
11108  field {String} The name of the field by which the Records are sorted
11109  direction {String} The sort order, "ASC" or "DESC"
11110      * </code></pre>
11111      */
11112     getSortState : function(){
11113         return this.sortInfo;
11114     },
11115
11116     // private
11117     applySort : function(){
11118         if(this.sortInfo && !this.remoteSort){
11119             var s = this.sortInfo, f = s.field;
11120             var st = this.fields.get(f).sortType;
11121             var fn = function(r1, r2){
11122                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11123                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11124             };
11125             this.data.sort(s.direction, fn);
11126             if(this.snapshot && this.snapshot != this.data){
11127                 this.snapshot.sort(s.direction, fn);
11128             }
11129         }
11130     },
11131
11132     /**
11133      * Sets the default sort column and order to be used by the next load operation.
11134      * @param {String} fieldName The name of the field to sort by.
11135      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11136      */
11137     setDefaultSort : function(field, dir){
11138         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11139     },
11140
11141     /**
11142      * Sort the Records.
11143      * If remote sorting is used, the sort is performed on the server, and the cache is
11144      * reloaded. If local sorting is used, the cache is sorted internally.
11145      * @param {String} fieldName The name of the field to sort by.
11146      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11147      */
11148     sort : function(fieldName, dir){
11149         var f = this.fields.get(fieldName);
11150         if(!dir){
11151             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11152             
11153             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11154                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11155             }else{
11156                 dir = f.sortDir;
11157             }
11158         }
11159         this.sortToggle[f.name] = dir;
11160         this.sortInfo = {field: f.name, direction: dir};
11161         if(!this.remoteSort){
11162             this.applySort();
11163             this.fireEvent("datachanged", this);
11164         }else{
11165             this.load(this.lastOptions);
11166         }
11167     },
11168
11169     /**
11170      * Calls the specified function for each of the Records in the cache.
11171      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11172      * Returning <em>false</em> aborts and exits the iteration.
11173      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11174      */
11175     each : function(fn, scope){
11176         this.data.each(fn, scope);
11177     },
11178
11179     /**
11180      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11181      * (e.g., during paging).
11182      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11183      */
11184     getModifiedRecords : function(){
11185         return this.modified;
11186     },
11187
11188     // private
11189     createFilterFn : function(property, value, anyMatch){
11190         if(!value.exec){ // not a regex
11191             value = String(value);
11192             if(value.length == 0){
11193                 return false;
11194             }
11195             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11196         }
11197         return function(r){
11198             return value.test(r.data[property]);
11199         };
11200     },
11201
11202     /**
11203      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11204      * @param {String} property A field on your records
11205      * @param {Number} start The record index to start at (defaults to 0)
11206      * @param {Number} end The last record index to include (defaults to length - 1)
11207      * @return {Number} The sum
11208      */
11209     sum : function(property, start, end){
11210         var rs = this.data.items, v = 0;
11211         start = start || 0;
11212         end = (end || end === 0) ? end : rs.length-1;
11213
11214         for(var i = start; i <= end; i++){
11215             v += (rs[i].data[property] || 0);
11216         }
11217         return v;
11218     },
11219
11220     /**
11221      * Filter the records by a specified property.
11222      * @param {String} field A field on your records
11223      * @param {String/RegExp} value Either a string that the field
11224      * should start with or a RegExp to test against the field
11225      * @param {Boolean} anyMatch True to match any part not just the beginning
11226      */
11227     filter : function(property, value, anyMatch){
11228         var fn = this.createFilterFn(property, value, anyMatch);
11229         return fn ? this.filterBy(fn) : this.clearFilter();
11230     },
11231
11232     /**
11233      * Filter by a function. The specified function will be called with each
11234      * record in this data source. If the function returns true the record is included,
11235      * otherwise it is filtered.
11236      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11237      * @param {Object} scope (optional) The scope of the function (defaults to this)
11238      */
11239     filterBy : function(fn, scope){
11240         this.snapshot = this.snapshot || this.data;
11241         this.data = this.queryBy(fn, scope||this);
11242         this.fireEvent("datachanged", this);
11243     },
11244
11245     /**
11246      * Query the records by a specified property.
11247      * @param {String} field A field on your records
11248      * @param {String/RegExp} value Either a string that the field
11249      * should start with or a RegExp to test against the field
11250      * @param {Boolean} anyMatch True to match any part not just the beginning
11251      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11252      */
11253     query : function(property, value, anyMatch){
11254         var fn = this.createFilterFn(property, value, anyMatch);
11255         return fn ? this.queryBy(fn) : this.data.clone();
11256     },
11257
11258     /**
11259      * Query by a function. The specified function will be called with each
11260      * record in this data source. If the function returns true the record is included
11261      * in the results.
11262      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11263      * @param {Object} scope (optional) The scope of the function (defaults to this)
11264       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11265      **/
11266     queryBy : function(fn, scope){
11267         var data = this.snapshot || this.data;
11268         return data.filterBy(fn, scope||this);
11269     },
11270
11271     /**
11272      * Collects unique values for a particular dataIndex from this store.
11273      * @param {String} dataIndex The property to collect
11274      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11275      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11276      * @return {Array} An array of the unique values
11277      **/
11278     collect : function(dataIndex, allowNull, bypassFilter){
11279         var d = (bypassFilter === true && this.snapshot) ?
11280                 this.snapshot.items : this.data.items;
11281         var v, sv, r = [], l = {};
11282         for(var i = 0, len = d.length; i < len; i++){
11283             v = d[i].data[dataIndex];
11284             sv = String(v);
11285             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11286                 l[sv] = true;
11287                 r[r.length] = v;
11288             }
11289         }
11290         return r;
11291     },
11292
11293     /**
11294      * Revert to a view of the Record cache with no filtering applied.
11295      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11296      */
11297     clearFilter : function(suppressEvent){
11298         if(this.snapshot && this.snapshot != this.data){
11299             this.data = this.snapshot;
11300             delete this.snapshot;
11301             if(suppressEvent !== true){
11302                 this.fireEvent("datachanged", this);
11303             }
11304         }
11305     },
11306
11307     // private
11308     afterEdit : function(record){
11309         if(this.modified.indexOf(record) == -1){
11310             this.modified.push(record);
11311         }
11312         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11313     },
11314     
11315     // private
11316     afterReject : function(record){
11317         this.modified.remove(record);
11318         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11319     },
11320
11321     // private
11322     afterCommit : function(record){
11323         this.modified.remove(record);
11324         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11325     },
11326
11327     /**
11328      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11329      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11330      */
11331     commitChanges : function(){
11332         var m = this.modified.slice(0);
11333         this.modified = [];
11334         for(var i = 0, len = m.length; i < len; i++){
11335             m[i].commit();
11336         }
11337     },
11338
11339     /**
11340      * Cancel outstanding changes on all changed records.
11341      */
11342     rejectChanges : function(){
11343         var m = this.modified.slice(0);
11344         this.modified = [];
11345         for(var i = 0, len = m.length; i < len; i++){
11346             m[i].reject();
11347         }
11348     },
11349
11350     onMetaChange : function(meta, rtype, o){
11351         this.recordType = rtype;
11352         this.fields = rtype.prototype.fields;
11353         delete this.snapshot;
11354         this.sortInfo = meta.sortInfo || this.sortInfo;
11355         this.modified = [];
11356         this.fireEvent('metachange', this, this.reader.meta);
11357     },
11358     
11359     moveIndex : function(data, type)
11360     {
11361         var index = this.indexOf(data);
11362         
11363         var newIndex = index + type;
11364         
11365         this.remove(data);
11366         
11367         this.insert(newIndex, data);
11368         
11369     }
11370 });/*
11371  * Based on:
11372  * Ext JS Library 1.1.1
11373  * Copyright(c) 2006-2007, Ext JS, LLC.
11374  *
11375  * Originally Released Under LGPL - original licence link has changed is not relivant.
11376  *
11377  * Fork - LGPL
11378  * <script type="text/javascript">
11379  */
11380
11381 /**
11382  * @class Roo.data.SimpleStore
11383  * @extends Roo.data.Store
11384  * Small helper class to make creating Stores from Array data easier.
11385  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11386  * @cfg {Array} fields An array of field definition objects, or field name strings.
11387  * @cfg {Array} data The multi-dimensional array of data
11388  * @constructor
11389  * @param {Object} config
11390  */
11391 Roo.data.SimpleStore = function(config){
11392     Roo.data.SimpleStore.superclass.constructor.call(this, {
11393         isLocal : true,
11394         reader: new Roo.data.ArrayReader({
11395                 id: config.id
11396             },
11397             Roo.data.Record.create(config.fields)
11398         ),
11399         proxy : new Roo.data.MemoryProxy(config.data)
11400     });
11401     this.load();
11402 };
11403 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11404  * Based on:
11405  * Ext JS Library 1.1.1
11406  * Copyright(c) 2006-2007, Ext JS, LLC.
11407  *
11408  * Originally Released Under LGPL - original licence link has changed is not relivant.
11409  *
11410  * Fork - LGPL
11411  * <script type="text/javascript">
11412  */
11413
11414 /**
11415 /**
11416  * @extends Roo.data.Store
11417  * @class Roo.data.JsonStore
11418  * Small helper class to make creating Stores for JSON data easier. <br/>
11419 <pre><code>
11420 var store = new Roo.data.JsonStore({
11421     url: 'get-images.php',
11422     root: 'images',
11423     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11424 });
11425 </code></pre>
11426  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11427  * JsonReader and HttpProxy (unless inline data is provided).</b>
11428  * @cfg {Array} fields An array of field definition objects, or field name strings.
11429  * @constructor
11430  * @param {Object} config
11431  */
11432 Roo.data.JsonStore = function(c){
11433     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11434         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11435         reader: new Roo.data.JsonReader(c, c.fields)
11436     }));
11437 };
11438 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11439  * Based on:
11440  * Ext JS Library 1.1.1
11441  * Copyright(c) 2006-2007, Ext JS, LLC.
11442  *
11443  * Originally Released Under LGPL - original licence link has changed is not relivant.
11444  *
11445  * Fork - LGPL
11446  * <script type="text/javascript">
11447  */
11448
11449  
11450 Roo.data.Field = function(config){
11451     if(typeof config == "string"){
11452         config = {name: config};
11453     }
11454     Roo.apply(this, config);
11455     
11456     if(!this.type){
11457         this.type = "auto";
11458     }
11459     
11460     var st = Roo.data.SortTypes;
11461     // named sortTypes are supported, here we look them up
11462     if(typeof this.sortType == "string"){
11463         this.sortType = st[this.sortType];
11464     }
11465     
11466     // set default sortType for strings and dates
11467     if(!this.sortType){
11468         switch(this.type){
11469             case "string":
11470                 this.sortType = st.asUCString;
11471                 break;
11472             case "date":
11473                 this.sortType = st.asDate;
11474                 break;
11475             default:
11476                 this.sortType = st.none;
11477         }
11478     }
11479
11480     // define once
11481     var stripRe = /[\$,%]/g;
11482
11483     // prebuilt conversion function for this field, instead of
11484     // switching every time we're reading a value
11485     if(!this.convert){
11486         var cv, dateFormat = this.dateFormat;
11487         switch(this.type){
11488             case "":
11489             case "auto":
11490             case undefined:
11491                 cv = function(v){ return v; };
11492                 break;
11493             case "string":
11494                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11495                 break;
11496             case "int":
11497                 cv = function(v){
11498                     return v !== undefined && v !== null && v !== '' ?
11499                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11500                     };
11501                 break;
11502             case "float":
11503                 cv = function(v){
11504                     return v !== undefined && v !== null && v !== '' ?
11505                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11506                     };
11507                 break;
11508             case "bool":
11509             case "boolean":
11510                 cv = function(v){ return v === true || v === "true" || v == 1; };
11511                 break;
11512             case "date":
11513                 cv = function(v){
11514                     if(!v){
11515                         return '';
11516                     }
11517                     if(v instanceof Date){
11518                         return v;
11519                     }
11520                     if(dateFormat){
11521                         if(dateFormat == "timestamp"){
11522                             return new Date(v*1000);
11523                         }
11524                         return Date.parseDate(v, dateFormat);
11525                     }
11526                     var parsed = Date.parse(v);
11527                     return parsed ? new Date(parsed) : null;
11528                 };
11529              break;
11530             
11531         }
11532         this.convert = cv;
11533     }
11534 };
11535
11536 Roo.data.Field.prototype = {
11537     dateFormat: null,
11538     defaultValue: "",
11539     mapping: null,
11540     sortType : null,
11541     sortDir : "ASC"
11542 };/*
11543  * Based on:
11544  * Ext JS Library 1.1.1
11545  * Copyright(c) 2006-2007, Ext JS, LLC.
11546  *
11547  * Originally Released Under LGPL - original licence link has changed is not relivant.
11548  *
11549  * Fork - LGPL
11550  * <script type="text/javascript">
11551  */
11552  
11553 // Base class for reading structured data from a data source.  This class is intended to be
11554 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11555
11556 /**
11557  * @class Roo.data.DataReader
11558  * Base class for reading structured data from a data source.  This class is intended to be
11559  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11560  */
11561
11562 Roo.data.DataReader = function(meta, recordType){
11563     
11564     this.meta = meta;
11565     
11566     this.recordType = recordType instanceof Array ? 
11567         Roo.data.Record.create(recordType) : recordType;
11568 };
11569
11570 Roo.data.DataReader.prototype = {
11571      /**
11572      * Create an empty record
11573      * @param {Object} data (optional) - overlay some values
11574      * @return {Roo.data.Record} record created.
11575      */
11576     newRow :  function(d) {
11577         var da =  {};
11578         this.recordType.prototype.fields.each(function(c) {
11579             switch( c.type) {
11580                 case 'int' : da[c.name] = 0; break;
11581                 case 'date' : da[c.name] = new Date(); break;
11582                 case 'float' : da[c.name] = 0.0; break;
11583                 case 'boolean' : da[c.name] = false; break;
11584                 default : da[c.name] = ""; break;
11585             }
11586             
11587         });
11588         return new this.recordType(Roo.apply(da, d));
11589     }
11590     
11591 };/*
11592  * Based on:
11593  * Ext JS Library 1.1.1
11594  * Copyright(c) 2006-2007, Ext JS, LLC.
11595  *
11596  * Originally Released Under LGPL - original licence link has changed is not relivant.
11597  *
11598  * Fork - LGPL
11599  * <script type="text/javascript">
11600  */
11601
11602 /**
11603  * @class Roo.data.DataProxy
11604  * @extends Roo.data.Observable
11605  * This class is an abstract base class for implementations which provide retrieval of
11606  * unformatted data objects.<br>
11607  * <p>
11608  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11609  * (of the appropriate type which knows how to parse the data object) to provide a block of
11610  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11611  * <p>
11612  * Custom implementations must implement the load method as described in
11613  * {@link Roo.data.HttpProxy#load}.
11614  */
11615 Roo.data.DataProxy = function(){
11616     this.addEvents({
11617         /**
11618          * @event beforeload
11619          * Fires before a network request is made to retrieve a data object.
11620          * @param {Object} This DataProxy object.
11621          * @param {Object} params The params parameter to the load function.
11622          */
11623         beforeload : true,
11624         /**
11625          * @event load
11626          * Fires before the load method's callback is called.
11627          * @param {Object} This DataProxy object.
11628          * @param {Object} o The data object.
11629          * @param {Object} arg The callback argument object passed to the load function.
11630          */
11631         load : true,
11632         /**
11633          * @event loadexception
11634          * Fires if an Exception occurs during data retrieval.
11635          * @param {Object} This DataProxy object.
11636          * @param {Object} o The data object.
11637          * @param {Object} arg The callback argument object passed to the load function.
11638          * @param {Object} e The Exception.
11639          */
11640         loadexception : true
11641     });
11642     Roo.data.DataProxy.superclass.constructor.call(this);
11643 };
11644
11645 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11646
11647     /**
11648      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11649      */
11650 /*
11651  * Based on:
11652  * Ext JS Library 1.1.1
11653  * Copyright(c) 2006-2007, Ext JS, LLC.
11654  *
11655  * Originally Released Under LGPL - original licence link has changed is not relivant.
11656  *
11657  * Fork - LGPL
11658  * <script type="text/javascript">
11659  */
11660 /**
11661  * @class Roo.data.MemoryProxy
11662  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11663  * to the Reader when its load method is called.
11664  * @constructor
11665  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11666  */
11667 Roo.data.MemoryProxy = function(data){
11668     if (data.data) {
11669         data = data.data;
11670     }
11671     Roo.data.MemoryProxy.superclass.constructor.call(this);
11672     this.data = data;
11673 };
11674
11675 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11676     
11677     /**
11678      * Load data from the requested source (in this case an in-memory
11679      * data object passed to the constructor), read the data object into
11680      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11681      * process that block using the passed callback.
11682      * @param {Object} params This parameter is not used by the MemoryProxy class.
11683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11684      * object into a block of Roo.data.Records.
11685      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11686      * The function must be passed <ul>
11687      * <li>The Record block object</li>
11688      * <li>The "arg" argument from the load function</li>
11689      * <li>A boolean success indicator</li>
11690      * </ul>
11691      * @param {Object} scope The scope in which to call the callback
11692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11693      */
11694     load : function(params, reader, callback, scope, arg){
11695         params = params || {};
11696         var result;
11697         try {
11698             result = reader.readRecords(this.data);
11699         }catch(e){
11700             this.fireEvent("loadexception", this, arg, null, e);
11701             callback.call(scope, null, arg, false);
11702             return;
11703         }
11704         callback.call(scope, result, arg, true);
11705     },
11706     
11707     // private
11708     update : function(params, records){
11709         
11710     }
11711 });/*
11712  * Based on:
11713  * Ext JS Library 1.1.1
11714  * Copyright(c) 2006-2007, Ext JS, LLC.
11715  *
11716  * Originally Released Under LGPL - original licence link has changed is not relivant.
11717  *
11718  * Fork - LGPL
11719  * <script type="text/javascript">
11720  */
11721 /**
11722  * @class Roo.data.HttpProxy
11723  * @extends Roo.data.DataProxy
11724  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11725  * configured to reference a certain URL.<br><br>
11726  * <p>
11727  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11728  * from which the running page was served.<br><br>
11729  * <p>
11730  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11731  * <p>
11732  * Be aware that to enable the browser to parse an XML document, the server must set
11733  * the Content-Type header in the HTTP response to "text/xml".
11734  * @constructor
11735  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11736  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11737  * will be used to make the request.
11738  */
11739 Roo.data.HttpProxy = function(conn){
11740     Roo.data.HttpProxy.superclass.constructor.call(this);
11741     // is conn a conn config or a real conn?
11742     this.conn = conn;
11743     this.useAjax = !conn || !conn.events;
11744   
11745 };
11746
11747 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11748     // thse are take from connection...
11749     
11750     /**
11751      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11752      */
11753     /**
11754      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11755      * extra parameters to each request made by this object. (defaults to undefined)
11756      */
11757     /**
11758      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11759      *  to each request made by this object. (defaults to undefined)
11760      */
11761     /**
11762      * @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)
11763      */
11764     /**
11765      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11766      */
11767      /**
11768      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11769      * @type Boolean
11770      */
11771   
11772
11773     /**
11774      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11775      * @type Boolean
11776      */
11777     /**
11778      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11779      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11780      * a finer-grained basis than the DataProxy events.
11781      */
11782     getConnection : function(){
11783         return this.useAjax ? Roo.Ajax : this.conn;
11784     },
11785
11786     /**
11787      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11788      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11789      * process that block using the passed callback.
11790      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11791      * for the request to the remote server.
11792      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11793      * object into a block of Roo.data.Records.
11794      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11795      * The function must be passed <ul>
11796      * <li>The Record block object</li>
11797      * <li>The "arg" argument from the load function</li>
11798      * <li>A boolean success indicator</li>
11799      * </ul>
11800      * @param {Object} scope The scope in which to call the callback
11801      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11802      */
11803     load : function(params, reader, callback, scope, arg){
11804         if(this.fireEvent("beforeload", this, params) !== false){
11805             var  o = {
11806                 params : params || {},
11807                 request: {
11808                     callback : callback,
11809                     scope : scope,
11810                     arg : arg
11811                 },
11812                 reader: reader,
11813                 callback : this.loadResponse,
11814                 scope: this
11815             };
11816             if(this.useAjax){
11817                 Roo.applyIf(o, this.conn);
11818                 if(this.activeRequest){
11819                     Roo.Ajax.abort(this.activeRequest);
11820                 }
11821                 this.activeRequest = Roo.Ajax.request(o);
11822             }else{
11823                 this.conn.request(o);
11824             }
11825         }else{
11826             callback.call(scope||this, null, arg, false);
11827         }
11828     },
11829
11830     // private
11831     loadResponse : function(o, success, response){
11832         delete this.activeRequest;
11833         if(!success){
11834             this.fireEvent("loadexception", this, o, response);
11835             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11836             return;
11837         }
11838         var result;
11839         try {
11840             result = o.reader.read(response);
11841         }catch(e){
11842             this.fireEvent("loadexception", this, o, response, e);
11843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11844             return;
11845         }
11846         
11847         this.fireEvent("load", this, o, o.request.arg);
11848         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11849     },
11850
11851     // private
11852     update : function(dataSet){
11853
11854     },
11855
11856     // private
11857     updateResponse : function(dataSet){
11858
11859     }
11860 });/*
11861  * Based on:
11862  * Ext JS Library 1.1.1
11863  * Copyright(c) 2006-2007, Ext JS, LLC.
11864  *
11865  * Originally Released Under LGPL - original licence link has changed is not relivant.
11866  *
11867  * Fork - LGPL
11868  * <script type="text/javascript">
11869  */
11870
11871 /**
11872  * @class Roo.data.ScriptTagProxy
11873  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11874  * other than the originating domain of the running page.<br><br>
11875  * <p>
11876  * <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
11877  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11878  * <p>
11879  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11880  * source code that is used as the source inside a &lt;script> tag.<br><br>
11881  * <p>
11882  * In order for the browser to process the returned data, the server must wrap the data object
11883  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11884  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11885  * depending on whether the callback name was passed:
11886  * <p>
11887  * <pre><code>
11888 boolean scriptTag = false;
11889 String cb = request.getParameter("callback");
11890 if (cb != null) {
11891     scriptTag = true;
11892     response.setContentType("text/javascript");
11893 } else {
11894     response.setContentType("application/x-json");
11895 }
11896 Writer out = response.getWriter();
11897 if (scriptTag) {
11898     out.write(cb + "(");
11899 }
11900 out.print(dataBlock.toJsonString());
11901 if (scriptTag) {
11902     out.write(");");
11903 }
11904 </pre></code>
11905  *
11906  * @constructor
11907  * @param {Object} config A configuration object.
11908  */
11909 Roo.data.ScriptTagProxy = function(config){
11910     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11911     Roo.apply(this, config);
11912     this.head = document.getElementsByTagName("head")[0];
11913 };
11914
11915 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11916
11917 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11918     /**
11919      * @cfg {String} url The URL from which to request the data object.
11920      */
11921     /**
11922      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11923      */
11924     timeout : 30000,
11925     /**
11926      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11927      * the server the name of the callback function set up by the load call to process the returned data object.
11928      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11929      * javascript output which calls this named function passing the data object as its only parameter.
11930      */
11931     callbackParam : "callback",
11932     /**
11933      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11934      * name to the request.
11935      */
11936     nocache : true,
11937
11938     /**
11939      * Load data from the configured URL, read the data object into
11940      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11941      * process that block using the passed callback.
11942      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11943      * for the request to the remote server.
11944      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11945      * object into a block of Roo.data.Records.
11946      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11947      * The function must be passed <ul>
11948      * <li>The Record block object</li>
11949      * <li>The "arg" argument from the load function</li>
11950      * <li>A boolean success indicator</li>
11951      * </ul>
11952      * @param {Object} scope The scope in which to call the callback
11953      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11954      */
11955     load : function(params, reader, callback, scope, arg){
11956         if(this.fireEvent("beforeload", this, params) !== false){
11957
11958             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11959
11960             var url = this.url;
11961             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11962             if(this.nocache){
11963                 url += "&_dc=" + (new Date().getTime());
11964             }
11965             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11966             var trans = {
11967                 id : transId,
11968                 cb : "stcCallback"+transId,
11969                 scriptId : "stcScript"+transId,
11970                 params : params,
11971                 arg : arg,
11972                 url : url,
11973                 callback : callback,
11974                 scope : scope,
11975                 reader : reader
11976             };
11977             var conn = this;
11978
11979             window[trans.cb] = function(o){
11980                 conn.handleResponse(o, trans);
11981             };
11982
11983             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11984
11985             if(this.autoAbort !== false){
11986                 this.abort();
11987             }
11988
11989             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11990
11991             var script = document.createElement("script");
11992             script.setAttribute("src", url);
11993             script.setAttribute("type", "text/javascript");
11994             script.setAttribute("id", trans.scriptId);
11995             this.head.appendChild(script);
11996
11997             this.trans = trans;
11998         }else{
11999             callback.call(scope||this, null, arg, false);
12000         }
12001     },
12002
12003     // private
12004     isLoading : function(){
12005         return this.trans ? true : false;
12006     },
12007
12008     /**
12009      * Abort the current server request.
12010      */
12011     abort : function(){
12012         if(this.isLoading()){
12013             this.destroyTrans(this.trans);
12014         }
12015     },
12016
12017     // private
12018     destroyTrans : function(trans, isLoaded){
12019         this.head.removeChild(document.getElementById(trans.scriptId));
12020         clearTimeout(trans.timeoutId);
12021         if(isLoaded){
12022             window[trans.cb] = undefined;
12023             try{
12024                 delete window[trans.cb];
12025             }catch(e){}
12026         }else{
12027             // if hasn't been loaded, wait for load to remove it to prevent script error
12028             window[trans.cb] = function(){
12029                 window[trans.cb] = undefined;
12030                 try{
12031                     delete window[trans.cb];
12032                 }catch(e){}
12033             };
12034         }
12035     },
12036
12037     // private
12038     handleResponse : function(o, trans){
12039         this.trans = false;
12040         this.destroyTrans(trans, true);
12041         var result;
12042         try {
12043             result = trans.reader.readRecords(o);
12044         }catch(e){
12045             this.fireEvent("loadexception", this, o, trans.arg, e);
12046             trans.callback.call(trans.scope||window, null, trans.arg, false);
12047             return;
12048         }
12049         this.fireEvent("load", this, o, trans.arg);
12050         trans.callback.call(trans.scope||window, result, trans.arg, true);
12051     },
12052
12053     // private
12054     handleFailure : function(trans){
12055         this.trans = false;
12056         this.destroyTrans(trans, false);
12057         this.fireEvent("loadexception", this, null, trans.arg);
12058         trans.callback.call(trans.scope||window, null, trans.arg, false);
12059     }
12060 });/*
12061  * Based on:
12062  * Ext JS Library 1.1.1
12063  * Copyright(c) 2006-2007, Ext JS, LLC.
12064  *
12065  * Originally Released Under LGPL - original licence link has changed is not relivant.
12066  *
12067  * Fork - LGPL
12068  * <script type="text/javascript">
12069  */
12070
12071 /**
12072  * @class Roo.data.JsonReader
12073  * @extends Roo.data.DataReader
12074  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12075  * based on mappings in a provided Roo.data.Record constructor.
12076  * 
12077  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12078  * in the reply previously. 
12079  * 
12080  * <p>
12081  * Example code:
12082  * <pre><code>
12083 var RecordDef = Roo.data.Record.create([
12084     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12085     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12086 ]);
12087 var myReader = new Roo.data.JsonReader({
12088     totalProperty: "results",    // The property which contains the total dataset size (optional)
12089     root: "rows",                // The property which contains an Array of row objects
12090     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12091 }, RecordDef);
12092 </code></pre>
12093  * <p>
12094  * This would consume a JSON file like this:
12095  * <pre><code>
12096 { 'results': 2, 'rows': [
12097     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12098     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12099 }
12100 </code></pre>
12101  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12102  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12103  * paged from the remote server.
12104  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12105  * @cfg {String} root name of the property which contains the Array of row objects.
12106  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12107  * @cfg {Array} fields Array of field definition objects
12108  * @constructor
12109  * Create a new JsonReader
12110  * @param {Object} meta Metadata configuration options
12111  * @param {Object} recordType Either an Array of field definition objects,
12112  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12113  */
12114 Roo.data.JsonReader = function(meta, recordType){
12115     
12116     meta = meta || {};
12117     // set some defaults:
12118     Roo.applyIf(meta, {
12119         totalProperty: 'total',
12120         successProperty : 'success',
12121         root : 'data',
12122         id : 'id'
12123     });
12124     
12125     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12126 };
12127 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12128     
12129     /**
12130      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12131      * Used by Store query builder to append _requestMeta to params.
12132      * 
12133      */
12134     metaFromRemote : false,
12135     /**
12136      * This method is only used by a DataProxy which has retrieved data from a remote server.
12137      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12138      * @return {Object} data A data block which is used by an Roo.data.Store object as
12139      * a cache of Roo.data.Records.
12140      */
12141     read : function(response){
12142         var json = response.responseText;
12143        
12144         var o = /* eval:var:o */ eval("("+json+")");
12145         if(!o) {
12146             throw {message: "JsonReader.read: Json object not found"};
12147         }
12148         
12149         if(o.metaData){
12150             
12151             delete this.ef;
12152             this.metaFromRemote = true;
12153             this.meta = o.metaData;
12154             this.recordType = Roo.data.Record.create(o.metaData.fields);
12155             this.onMetaChange(this.meta, this.recordType, o);
12156         }
12157         return this.readRecords(o);
12158     },
12159
12160     // private function a store will implement
12161     onMetaChange : function(meta, recordType, o){
12162
12163     },
12164
12165     /**
12166          * @ignore
12167          */
12168     simpleAccess: function(obj, subsc) {
12169         return obj[subsc];
12170     },
12171
12172         /**
12173          * @ignore
12174          */
12175     getJsonAccessor: function(){
12176         var re = /[\[\.]/;
12177         return function(expr) {
12178             try {
12179                 return(re.test(expr))
12180                     ? new Function("obj", "return obj." + expr)
12181                     : function(obj){
12182                         return obj[expr];
12183                     };
12184             } catch(e){}
12185             return Roo.emptyFn;
12186         };
12187     }(),
12188
12189     /**
12190      * Create a data block containing Roo.data.Records from an XML document.
12191      * @param {Object} o An object which contains an Array of row objects in the property specified
12192      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12193      * which contains the total size of the dataset.
12194      * @return {Object} data A data block which is used by an Roo.data.Store object as
12195      * a cache of Roo.data.Records.
12196      */
12197     readRecords : function(o){
12198         /**
12199          * After any data loads, the raw JSON data is available for further custom processing.
12200          * @type Object
12201          */
12202         this.o = o;
12203         var s = this.meta, Record = this.recordType,
12204             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12205
12206 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12207         if (!this.ef) {
12208             if(s.totalProperty) {
12209                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12210                 }
12211                 if(s.successProperty) {
12212                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12213                 }
12214                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12215                 if (s.id) {
12216                         var g = this.getJsonAccessor(s.id);
12217                         this.getId = function(rec) {
12218                                 var r = g(rec);  
12219                                 return (r === undefined || r === "") ? null : r;
12220                         };
12221                 } else {
12222                         this.getId = function(){return null;};
12223                 }
12224             this.ef = [];
12225             for(var jj = 0; jj < fl; jj++){
12226                 f = fi[jj];
12227                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12228                 this.ef[jj] = this.getJsonAccessor(map);
12229             }
12230         }
12231
12232         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12233         if(s.totalProperty){
12234             var vt = parseInt(this.getTotal(o), 10);
12235             if(!isNaN(vt)){
12236                 totalRecords = vt;
12237             }
12238         }
12239         if(s.successProperty){
12240             var vs = this.getSuccess(o);
12241             if(vs === false || vs === 'false'){
12242                 success = false;
12243             }
12244         }
12245         var records = [];
12246         for(var i = 0; i < c; i++){
12247                 var n = root[i];
12248             var values = {};
12249             var id = this.getId(n);
12250             for(var j = 0; j < fl; j++){
12251                 f = fi[j];
12252             var v = this.ef[j](n);
12253             if (!f.convert) {
12254                 Roo.log('missing convert for ' + f.name);
12255                 Roo.log(f);
12256                 continue;
12257             }
12258             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12259             }
12260             var record = new Record(values, id);
12261             record.json = n;
12262             records[i] = record;
12263         }
12264         return {
12265             raw : o,
12266             success : success,
12267             records : records,
12268             totalRecords : totalRecords
12269         };
12270     }
12271 });/*
12272  * Based on:
12273  * Ext JS Library 1.1.1
12274  * Copyright(c) 2006-2007, Ext JS, LLC.
12275  *
12276  * Originally Released Under LGPL - original licence link has changed is not relivant.
12277  *
12278  * Fork - LGPL
12279  * <script type="text/javascript">
12280  */
12281
12282 /**
12283  * @class Roo.data.ArrayReader
12284  * @extends Roo.data.DataReader
12285  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12286  * Each element of that Array represents a row of data fields. The
12287  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12288  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12289  * <p>
12290  * Example code:.
12291  * <pre><code>
12292 var RecordDef = Roo.data.Record.create([
12293     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12294     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12295 ]);
12296 var myReader = new Roo.data.ArrayReader({
12297     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12298 }, RecordDef);
12299 </code></pre>
12300  * <p>
12301  * This would consume an Array like this:
12302  * <pre><code>
12303 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12304   </code></pre>
12305  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12306  * @constructor
12307  * Create a new JsonReader
12308  * @param {Object} meta Metadata configuration options.
12309  * @param {Object} recordType Either an Array of field definition objects
12310  * as specified to {@link Roo.data.Record#create},
12311  * or an {@link Roo.data.Record} object
12312  * created using {@link Roo.data.Record#create}.
12313  */
12314 Roo.data.ArrayReader = function(meta, recordType){
12315     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12316 };
12317
12318 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12319     /**
12320      * Create a data block containing Roo.data.Records from an XML document.
12321      * @param {Object} o An Array of row objects which represents the dataset.
12322      * @return {Object} data A data block which is used by an Roo.data.Store object as
12323      * a cache of Roo.data.Records.
12324      */
12325     readRecords : function(o){
12326         var sid = this.meta ? this.meta.id : null;
12327         var recordType = this.recordType, fields = recordType.prototype.fields;
12328         var records = [];
12329         var root = o;
12330             for(var i = 0; i < root.length; i++){
12331                     var n = root[i];
12332                 var values = {};
12333                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12334                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12335                 var f = fields.items[j];
12336                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12337                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12338                 v = f.convert(v);
12339                 values[f.name] = v;
12340             }
12341                 var record = new recordType(values, id);
12342                 record.json = n;
12343                 records[records.length] = record;
12344             }
12345             return {
12346                 records : records,
12347                 totalRecords : records.length
12348             };
12349     }
12350 });/*
12351  * - LGPL
12352  * * 
12353  */
12354
12355 /**
12356  * @class Roo.bootstrap.ComboBox
12357  * @extends Roo.bootstrap.TriggerField
12358  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12359  * @cfg {Boolean} append (true|false) default false
12360  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12361  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12362  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12363  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12364  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12365  * @cfg {Boolean} animate default true
12366  * @cfg {Boolean} emptyResultText only for touch device
12367  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12368  * @cfg {String} emptyTitle default ''
12369  * @constructor
12370  * Create a new ComboBox.
12371  * @param {Object} config Configuration options
12372  */
12373 Roo.bootstrap.ComboBox = function(config){
12374     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12375     this.addEvents({
12376         /**
12377          * @event expand
12378          * Fires when the dropdown list is expanded
12379         * @param {Roo.bootstrap.ComboBox} combo This combo box
12380         */
12381         'expand' : true,
12382         /**
12383          * @event collapse
12384          * Fires when the dropdown list is collapsed
12385         * @param {Roo.bootstrap.ComboBox} combo This combo box
12386         */
12387         'collapse' : true,
12388         /**
12389          * @event beforeselect
12390          * Fires before a list item is selected. Return false to cancel the selection.
12391         * @param {Roo.bootstrap.ComboBox} combo This combo box
12392         * @param {Roo.data.Record} record The data record returned from the underlying store
12393         * @param {Number} index The index of the selected item in the dropdown list
12394         */
12395         'beforeselect' : true,
12396         /**
12397          * @event select
12398          * Fires when a list item is selected
12399         * @param {Roo.bootstrap.ComboBox} combo This combo box
12400         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12401         * @param {Number} index The index of the selected item in the dropdown list
12402         */
12403         'select' : true,
12404         /**
12405          * @event beforequery
12406          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12407          * The event object passed has these properties:
12408         * @param {Roo.bootstrap.ComboBox} combo This combo box
12409         * @param {String} query The query
12410         * @param {Boolean} forceAll true to force "all" query
12411         * @param {Boolean} cancel true to cancel the query
12412         * @param {Object} e The query event object
12413         */
12414         'beforequery': true,
12415          /**
12416          * @event add
12417          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12418         * @param {Roo.bootstrap.ComboBox} combo This combo box
12419         */
12420         'add' : true,
12421         /**
12422          * @event edit
12423          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12424         * @param {Roo.bootstrap.ComboBox} combo This combo box
12425         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12426         */
12427         'edit' : true,
12428         /**
12429          * @event remove
12430          * Fires when the remove value from the combobox array
12431         * @param {Roo.bootstrap.ComboBox} combo This combo box
12432         */
12433         'remove' : true,
12434         /**
12435          * @event afterremove
12436          * Fires when the remove value from the combobox array
12437         * @param {Roo.bootstrap.ComboBox} combo This combo box
12438         */
12439         'afterremove' : true,
12440         /**
12441          * @event specialfilter
12442          * Fires when specialfilter
12443             * @param {Roo.bootstrap.ComboBox} combo This combo box
12444             */
12445         'specialfilter' : true,
12446         /**
12447          * @event tick
12448          * Fires when tick the element
12449             * @param {Roo.bootstrap.ComboBox} combo This combo box
12450             */
12451         'tick' : true,
12452         /**
12453          * @event touchviewdisplay
12454          * Fires when touch view require special display (default is using displayField)
12455             * @param {Roo.bootstrap.ComboBox} combo This combo box
12456             * @param {Object} cfg set html .
12457             */
12458         'touchviewdisplay' : true
12459         
12460     });
12461     
12462     this.item = [];
12463     this.tickItems = [];
12464     
12465     this.selectedIndex = -1;
12466     if(this.mode == 'local'){
12467         if(config.queryDelay === undefined){
12468             this.queryDelay = 10;
12469         }
12470         if(config.minChars === undefined){
12471             this.minChars = 0;
12472         }
12473     }
12474 };
12475
12476 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12477      
12478     /**
12479      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12480      * rendering into an Roo.Editor, defaults to false)
12481      */
12482     /**
12483      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12484      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12485      */
12486     /**
12487      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12488      */
12489     /**
12490      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12491      * the dropdown list (defaults to undefined, with no header element)
12492      */
12493
12494      /**
12495      * @cfg {String/Roo.Template} tpl The template to use to render the output
12496      */
12497      
12498      /**
12499      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12500      */
12501     listWidth: undefined,
12502     /**
12503      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12504      * mode = 'remote' or 'text' if mode = 'local')
12505      */
12506     displayField: undefined,
12507     
12508     /**
12509      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12510      * mode = 'remote' or 'value' if mode = 'local'). 
12511      * Note: use of a valueField requires the user make a selection
12512      * in order for a value to be mapped.
12513      */
12514     valueField: undefined,
12515     /**
12516      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12517      */
12518     modalTitle : '',
12519     
12520     /**
12521      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12522      * field's data value (defaults to the underlying DOM element's name)
12523      */
12524     hiddenName: undefined,
12525     /**
12526      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12527      */
12528     listClass: '',
12529     /**
12530      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12531      */
12532     selectedClass: 'active',
12533     
12534     /**
12535      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12536      */
12537     shadow:'sides',
12538     /**
12539      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12540      * anchor positions (defaults to 'tl-bl')
12541      */
12542     listAlign: 'tl-bl?',
12543     /**
12544      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12545      */
12546     maxHeight: 300,
12547     /**
12548      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12549      * query specified by the allQuery config option (defaults to 'query')
12550      */
12551     triggerAction: 'query',
12552     /**
12553      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12554      * (defaults to 4, does not apply if editable = false)
12555      */
12556     minChars : 4,
12557     /**
12558      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12559      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12560      */
12561     typeAhead: false,
12562     /**
12563      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12564      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12565      */
12566     queryDelay: 500,
12567     /**
12568      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12569      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12570      */
12571     pageSize: 0,
12572     /**
12573      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12574      * when editable = true (defaults to false)
12575      */
12576     selectOnFocus:false,
12577     /**
12578      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12579      */
12580     queryParam: 'query',
12581     /**
12582      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12583      * when mode = 'remote' (defaults to 'Loading...')
12584      */
12585     loadingText: 'Loading...',
12586     /**
12587      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12588      */
12589     resizable: false,
12590     /**
12591      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12592      */
12593     handleHeight : 8,
12594     /**
12595      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12596      * traditional select (defaults to true)
12597      */
12598     editable: true,
12599     /**
12600      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12601      */
12602     allQuery: '',
12603     /**
12604      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12605      */
12606     mode: 'remote',
12607     /**
12608      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12609      * listWidth has a higher value)
12610      */
12611     minListWidth : 70,
12612     /**
12613      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12614      * allow the user to set arbitrary text into the field (defaults to false)
12615      */
12616     forceSelection:false,
12617     /**
12618      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12619      * if typeAhead = true (defaults to 250)
12620      */
12621     typeAheadDelay : 250,
12622     /**
12623      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12624      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12625      */
12626     valueNotFoundText : undefined,
12627     /**
12628      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12629      */
12630     blockFocus : false,
12631     
12632     /**
12633      * @cfg {Boolean} disableClear Disable showing of clear button.
12634      */
12635     disableClear : false,
12636     /**
12637      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12638      */
12639     alwaysQuery : false,
12640     
12641     /**
12642      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12643      */
12644     multiple : false,
12645     
12646     /**
12647      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12648      */
12649     invalidClass : "has-warning",
12650     
12651     /**
12652      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12653      */
12654     validClass : "has-success",
12655     
12656     /**
12657      * @cfg {Boolean} specialFilter (true|false) special filter default false
12658      */
12659     specialFilter : false,
12660     
12661     /**
12662      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12663      */
12664     mobileTouchView : true,
12665     
12666     /**
12667      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12668      */
12669     useNativeIOS : false,
12670     
12671     ios_options : false,
12672     
12673     //private
12674     addicon : false,
12675     editicon: false,
12676     
12677     page: 0,
12678     hasQuery: false,
12679     append: false,
12680     loadNext: false,
12681     autoFocus : true,
12682     tickable : false,
12683     btnPosition : 'right',
12684     triggerList : true,
12685     showToggleBtn : true,
12686     animate : true,
12687     emptyResultText: 'Empty',
12688     triggerText : 'Select',
12689     emptyTitle : '',
12690     
12691     // element that contains real text value.. (when hidden is used..)
12692     
12693     getAutoCreate : function()
12694     {   
12695         var cfg = false;
12696         //render
12697         /*
12698          * Render classic select for iso
12699          */
12700         
12701         if(Roo.isIOS && this.useNativeIOS){
12702             cfg = this.getAutoCreateNativeIOS();
12703             return cfg;
12704         }
12705         
12706         /*
12707          * Touch Devices
12708          */
12709         
12710         if(Roo.isTouch && this.mobileTouchView){
12711             cfg = this.getAutoCreateTouchView();
12712             return cfg;;
12713         }
12714         
12715         /*
12716          *  Normal ComboBox
12717          */
12718         if(!this.tickable){
12719             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12720             return cfg;
12721         }
12722         
12723         /*
12724          *  ComboBox with tickable selections
12725          */
12726              
12727         var align = this.labelAlign || this.parentLabelAlign();
12728         
12729         cfg = {
12730             cls : 'form-group roo-combobox-tickable' //input-group
12731         };
12732         
12733         var btn_text_select = '';
12734         var btn_text_done = '';
12735         var btn_text_cancel = '';
12736         
12737         if (this.btn_text_show) {
12738             btn_text_select = 'Select';
12739             btn_text_done = 'Done';
12740             btn_text_cancel = 'Cancel'; 
12741         }
12742         
12743         var buttons = {
12744             tag : 'div',
12745             cls : 'tickable-buttons',
12746             cn : [
12747                 {
12748                     tag : 'button',
12749                     type : 'button',
12750                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12751                     //html : this.triggerText
12752                     html: btn_text_select
12753                 },
12754                 {
12755                     tag : 'button',
12756                     type : 'button',
12757                     name : 'ok',
12758                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12759                     //html : 'Done'
12760                     html: btn_text_done
12761                 },
12762                 {
12763                     tag : 'button',
12764                     type : 'button',
12765                     name : 'cancel',
12766                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12767                     //html : 'Cancel'
12768                     html: btn_text_cancel
12769                 }
12770             ]
12771         };
12772         
12773         if(this.editable){
12774             buttons.cn.unshift({
12775                 tag: 'input',
12776                 cls: 'roo-select2-search-field-input'
12777             });
12778         }
12779         
12780         var _this = this;
12781         
12782         Roo.each(buttons.cn, function(c){
12783             if (_this.size) {
12784                 c.cls += ' btn-' + _this.size;
12785             }
12786
12787             if (_this.disabled) {
12788                 c.disabled = true;
12789             }
12790         });
12791         
12792         var box = {
12793             tag: 'div',
12794             cn: [
12795                 {
12796                     tag: 'input',
12797                     type : 'hidden',
12798                     cls: 'form-hidden-field'
12799                 },
12800                 {
12801                     tag: 'ul',
12802                     cls: 'roo-select2-choices',
12803                     cn:[
12804                         {
12805                             tag: 'li',
12806                             cls: 'roo-select2-search-field',
12807                             cn: [
12808                                 buttons
12809                             ]
12810                         }
12811                     ]
12812                 }
12813             ]
12814         };
12815         
12816         var combobox = {
12817             cls: 'roo-select2-container input-group roo-select2-container-multi',
12818             cn: [
12819                 box
12820 //                {
12821 //                    tag: 'ul',
12822 //                    cls: 'typeahead typeahead-long dropdown-menu',
12823 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12824 //                }
12825             ]
12826         };
12827         
12828         if(this.hasFeedback && !this.allowBlank){
12829             
12830             var feedback = {
12831                 tag: 'span',
12832                 cls: 'glyphicon form-control-feedback'
12833             };
12834
12835             combobox.cn.push(feedback);
12836         }
12837         
12838         
12839         if (align ==='left' && this.fieldLabel.length) {
12840             
12841             cfg.cls += ' roo-form-group-label-left';
12842             
12843             cfg.cn = [
12844                 {
12845                     tag : 'i',
12846                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12847                     tooltip : 'This field is required'
12848                 },
12849                 {
12850                     tag: 'label',
12851                     'for' :  id,
12852                     cls : 'control-label',
12853                     html : this.fieldLabel
12854
12855                 },
12856                 {
12857                     cls : "", 
12858                     cn: [
12859                         combobox
12860                     ]
12861                 }
12862
12863             ];
12864             
12865             var labelCfg = cfg.cn[1];
12866             var contentCfg = cfg.cn[2];
12867             
12868
12869             if(this.indicatorpos == 'right'){
12870                 
12871                 cfg.cn = [
12872                     {
12873                         tag: 'label',
12874                         'for' :  id,
12875                         cls : 'control-label',
12876                         cn : [
12877                             {
12878                                 tag : 'span',
12879                                 html : this.fieldLabel
12880                             },
12881                             {
12882                                 tag : 'i',
12883                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12884                                 tooltip : 'This field is required'
12885                             }
12886                         ]
12887                     },
12888                     {
12889                         cls : "",
12890                         cn: [
12891                             combobox
12892                         ]
12893                     }
12894
12895                 ];
12896                 
12897                 
12898                 
12899                 labelCfg = cfg.cn[0];
12900                 contentCfg = cfg.cn[1];
12901             
12902             }
12903             
12904             if(this.labelWidth > 12){
12905                 labelCfg.style = "width: " + this.labelWidth + 'px';
12906             }
12907             
12908             if(this.labelWidth < 13 && this.labelmd == 0){
12909                 this.labelmd = this.labelWidth;
12910             }
12911             
12912             if(this.labellg > 0){
12913                 labelCfg.cls += ' col-lg-' + this.labellg;
12914                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12915             }
12916             
12917             if(this.labelmd > 0){
12918                 labelCfg.cls += ' col-md-' + this.labelmd;
12919                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12920             }
12921             
12922             if(this.labelsm > 0){
12923                 labelCfg.cls += ' col-sm-' + this.labelsm;
12924                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12925             }
12926             
12927             if(this.labelxs > 0){
12928                 labelCfg.cls += ' col-xs-' + this.labelxs;
12929                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12930             }
12931                 
12932                 
12933         } else if ( this.fieldLabel.length) {
12934 //                Roo.log(" label");
12935                  cfg.cn = [
12936                     {
12937                         tag : 'i',
12938                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12939                         tooltip : 'This field is required'
12940                     },
12941                     {
12942                         tag: 'label',
12943                         //cls : 'input-group-addon',
12944                         html : this.fieldLabel
12945                     },
12946                     combobox
12947                 ];
12948                 
12949                 if(this.indicatorpos == 'right'){
12950                     cfg.cn = [
12951                         {
12952                             tag: 'label',
12953                             //cls : 'input-group-addon',
12954                             html : this.fieldLabel
12955                         },
12956                         {
12957                             tag : 'i',
12958                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12959                             tooltip : 'This field is required'
12960                         },
12961                         combobox
12962                     ];
12963                     
12964                 }
12965
12966         } else {
12967             
12968 //                Roo.log(" no label && no align");
12969                 cfg = combobox
12970                      
12971                 
12972         }
12973          
12974         var settings=this;
12975         ['xs','sm','md','lg'].map(function(size){
12976             if (settings[size]) {
12977                 cfg.cls += ' col-' + size + '-' + settings[size];
12978             }
12979         });
12980         
12981         return cfg;
12982         
12983     },
12984     
12985     _initEventsCalled : false,
12986     
12987     // private
12988     initEvents: function()
12989     {   
12990         if (this._initEventsCalled) { // as we call render... prevent looping...
12991             return;
12992         }
12993         this._initEventsCalled = true;
12994         
12995         if (!this.store) {
12996             throw "can not find store for combo";
12997         }
12998         
12999         this.store = Roo.factory(this.store, Roo.data);
13000         this.store.parent = this;
13001         
13002         // if we are building from html. then this element is so complex, that we can not really
13003         // use the rendered HTML.
13004         // so we have to trash and replace the previous code.
13005         if (Roo.XComponent.build_from_html) {
13006             // remove this element....
13007             var e = this.el.dom, k=0;
13008             while (e ) { e = e.previousSibling;  ++k;}
13009
13010             this.el.remove();
13011             
13012             this.el=false;
13013             this.rendered = false;
13014             
13015             this.render(this.parent().getChildContainer(true), k);
13016         }
13017         
13018         if(Roo.isIOS && this.useNativeIOS){
13019             this.initIOSView();
13020             return;
13021         }
13022         
13023         /*
13024          * Touch Devices
13025          */
13026         
13027         if(Roo.isTouch && this.mobileTouchView){
13028             this.initTouchView();
13029             return;
13030         }
13031         
13032         if(this.tickable){
13033             this.initTickableEvents();
13034             return;
13035         }
13036         
13037         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13038         
13039         if(this.hiddenName){
13040             
13041             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13042             
13043             this.hiddenField.dom.value =
13044                 this.hiddenValue !== undefined ? this.hiddenValue :
13045                 this.value !== undefined ? this.value : '';
13046
13047             // prevent input submission
13048             this.el.dom.removeAttribute('name');
13049             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13050              
13051              
13052         }
13053         //if(Roo.isGecko){
13054         //    this.el.dom.setAttribute('autocomplete', 'off');
13055         //}
13056         
13057         var cls = 'x-combo-list';
13058         
13059         //this.list = new Roo.Layer({
13060         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13061         //});
13062         
13063         var _this = this;
13064         
13065         (function(){
13066             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13067             _this.list.setWidth(lw);
13068         }).defer(100);
13069         
13070         this.list.on('mouseover', this.onViewOver, this);
13071         this.list.on('mousemove', this.onViewMove, this);
13072         this.list.on('scroll', this.onViewScroll, this);
13073         
13074         /*
13075         this.list.swallowEvent('mousewheel');
13076         this.assetHeight = 0;
13077
13078         if(this.title){
13079             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13080             this.assetHeight += this.header.getHeight();
13081         }
13082
13083         this.innerList = this.list.createChild({cls:cls+'-inner'});
13084         this.innerList.on('mouseover', this.onViewOver, this);
13085         this.innerList.on('mousemove', this.onViewMove, this);
13086         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13087         
13088         if(this.allowBlank && !this.pageSize && !this.disableClear){
13089             this.footer = this.list.createChild({cls:cls+'-ft'});
13090             this.pageTb = new Roo.Toolbar(this.footer);
13091            
13092         }
13093         if(this.pageSize){
13094             this.footer = this.list.createChild({cls:cls+'-ft'});
13095             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13096                     {pageSize: this.pageSize});
13097             
13098         }
13099         
13100         if (this.pageTb && this.allowBlank && !this.disableClear) {
13101             var _this = this;
13102             this.pageTb.add(new Roo.Toolbar.Fill(), {
13103                 cls: 'x-btn-icon x-btn-clear',
13104                 text: '&#160;',
13105                 handler: function()
13106                 {
13107                     _this.collapse();
13108                     _this.clearValue();
13109                     _this.onSelect(false, -1);
13110                 }
13111             });
13112         }
13113         if (this.footer) {
13114             this.assetHeight += this.footer.getHeight();
13115         }
13116         */
13117             
13118         if(!this.tpl){
13119             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13120         }
13121
13122         this.view = new Roo.View(this.list, this.tpl, {
13123             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13124         });
13125         //this.view.wrapEl.setDisplayed(false);
13126         this.view.on('click', this.onViewClick, this);
13127         
13128         
13129         this.store.on('beforeload', this.onBeforeLoad, this);
13130         this.store.on('load', this.onLoad, this);
13131         this.store.on('loadexception', this.onLoadException, this);
13132         /*
13133         if(this.resizable){
13134             this.resizer = new Roo.Resizable(this.list,  {
13135                pinned:true, handles:'se'
13136             });
13137             this.resizer.on('resize', function(r, w, h){
13138                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13139                 this.listWidth = w;
13140                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13141                 this.restrictHeight();
13142             }, this);
13143             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13144         }
13145         */
13146         if(!this.editable){
13147             this.editable = true;
13148             this.setEditable(false);
13149         }
13150         
13151         /*
13152         
13153         if (typeof(this.events.add.listeners) != 'undefined') {
13154             
13155             this.addicon = this.wrap.createChild(
13156                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13157        
13158             this.addicon.on('click', function(e) {
13159                 this.fireEvent('add', this);
13160             }, this);
13161         }
13162         if (typeof(this.events.edit.listeners) != 'undefined') {
13163             
13164             this.editicon = this.wrap.createChild(
13165                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13166             if (this.addicon) {
13167                 this.editicon.setStyle('margin-left', '40px');
13168             }
13169             this.editicon.on('click', function(e) {
13170                 
13171                 // we fire even  if inothing is selected..
13172                 this.fireEvent('edit', this, this.lastData );
13173                 
13174             }, this);
13175         }
13176         */
13177         
13178         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13179             "up" : function(e){
13180                 this.inKeyMode = true;
13181                 this.selectPrev();
13182             },
13183
13184             "down" : function(e){
13185                 if(!this.isExpanded()){
13186                     this.onTriggerClick();
13187                 }else{
13188                     this.inKeyMode = true;
13189                     this.selectNext();
13190                 }
13191             },
13192
13193             "enter" : function(e){
13194 //                this.onViewClick();
13195                 //return true;
13196                 this.collapse();
13197                 
13198                 if(this.fireEvent("specialkey", this, e)){
13199                     this.onViewClick(false);
13200                 }
13201                 
13202                 return true;
13203             },
13204
13205             "esc" : function(e){
13206                 this.collapse();
13207             },
13208
13209             "tab" : function(e){
13210                 this.collapse();
13211                 
13212                 if(this.fireEvent("specialkey", this, e)){
13213                     this.onViewClick(false);
13214                 }
13215                 
13216                 return true;
13217             },
13218
13219             scope : this,
13220
13221             doRelay : function(foo, bar, hname){
13222                 if(hname == 'down' || this.scope.isExpanded()){
13223                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13224                 }
13225                 return true;
13226             },
13227
13228             forceKeyDown: true
13229         });
13230         
13231         
13232         this.queryDelay = Math.max(this.queryDelay || 10,
13233                 this.mode == 'local' ? 10 : 250);
13234         
13235         
13236         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13237         
13238         if(this.typeAhead){
13239             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13240         }
13241         if(this.editable !== false){
13242             this.inputEl().on("keyup", this.onKeyUp, this);
13243         }
13244         if(this.forceSelection){
13245             this.inputEl().on('blur', this.doForce, this);
13246         }
13247         
13248         if(this.multiple){
13249             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13250             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13251         }
13252     },
13253     
13254     initTickableEvents: function()
13255     {   
13256         this.createList();
13257         
13258         if(this.hiddenName){
13259             
13260             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13261             
13262             this.hiddenField.dom.value =
13263                 this.hiddenValue !== undefined ? this.hiddenValue :
13264                 this.value !== undefined ? this.value : '';
13265
13266             // prevent input submission
13267             this.el.dom.removeAttribute('name');
13268             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13269              
13270              
13271         }
13272         
13273 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13274         
13275         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13276         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13277         if(this.triggerList){
13278             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13279         }
13280          
13281         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13282         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13283         
13284         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13285         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13286         
13287         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13288         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13289         
13290         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13291         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13292         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13293         
13294         this.okBtn.hide();
13295         this.cancelBtn.hide();
13296         
13297         var _this = this;
13298         
13299         (function(){
13300             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13301             _this.list.setWidth(lw);
13302         }).defer(100);
13303         
13304         this.list.on('mouseover', this.onViewOver, this);
13305         this.list.on('mousemove', this.onViewMove, this);
13306         
13307         this.list.on('scroll', this.onViewScroll, this);
13308         
13309         if(!this.tpl){
13310             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>';
13311         }
13312
13313         this.view = new Roo.View(this.list, this.tpl, {
13314             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13315         });
13316         
13317         //this.view.wrapEl.setDisplayed(false);
13318         this.view.on('click', this.onViewClick, this);
13319         
13320         
13321         
13322         this.store.on('beforeload', this.onBeforeLoad, this);
13323         this.store.on('load', this.onLoad, this);
13324         this.store.on('loadexception', this.onLoadException, this);
13325         
13326         if(this.editable){
13327             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13328                 "up" : function(e){
13329                     this.inKeyMode = true;
13330                     this.selectPrev();
13331                 },
13332
13333                 "down" : function(e){
13334                     this.inKeyMode = true;
13335                     this.selectNext();
13336                 },
13337
13338                 "enter" : function(e){
13339                     if(this.fireEvent("specialkey", this, e)){
13340                         this.onViewClick(false);
13341                     }
13342                     
13343                     return true;
13344                 },
13345
13346                 "esc" : function(e){
13347                     this.onTickableFooterButtonClick(e, false, false);
13348                 },
13349
13350                 "tab" : function(e){
13351                     this.fireEvent("specialkey", this, e);
13352                     
13353                     this.onTickableFooterButtonClick(e, false, false);
13354                     
13355                     return true;
13356                 },
13357
13358                 scope : this,
13359
13360                 doRelay : function(e, fn, key){
13361                     if(this.scope.isExpanded()){
13362                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13363                     }
13364                     return true;
13365                 },
13366
13367                 forceKeyDown: true
13368             });
13369         }
13370         
13371         this.queryDelay = Math.max(this.queryDelay || 10,
13372                 this.mode == 'local' ? 10 : 250);
13373         
13374         
13375         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13376         
13377         if(this.typeAhead){
13378             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13379         }
13380         
13381         if(this.editable !== false){
13382             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13383         }
13384         
13385         this.indicator = this.indicatorEl();
13386         
13387         if(this.indicator){
13388             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13389             this.indicator.hide();
13390         }
13391         
13392     },
13393
13394     onDestroy : function(){
13395         if(this.view){
13396             this.view.setStore(null);
13397             this.view.el.removeAllListeners();
13398             this.view.el.remove();
13399             this.view.purgeListeners();
13400         }
13401         if(this.list){
13402             this.list.dom.innerHTML  = '';
13403         }
13404         
13405         if(this.store){
13406             this.store.un('beforeload', this.onBeforeLoad, this);
13407             this.store.un('load', this.onLoad, this);
13408             this.store.un('loadexception', this.onLoadException, this);
13409         }
13410         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13411     },
13412
13413     // private
13414     fireKey : function(e){
13415         if(e.isNavKeyPress() && !this.list.isVisible()){
13416             this.fireEvent("specialkey", this, e);
13417         }
13418     },
13419
13420     // private
13421     onResize: function(w, h){
13422 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13423 //        
13424 //        if(typeof w != 'number'){
13425 //            // we do not handle it!?!?
13426 //            return;
13427 //        }
13428 //        var tw = this.trigger.getWidth();
13429 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13430 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13431 //        var x = w - tw;
13432 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13433 //            
13434 //        //this.trigger.setStyle('left', x+'px');
13435 //        
13436 //        if(this.list && this.listWidth === undefined){
13437 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13438 //            this.list.setWidth(lw);
13439 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13440 //        }
13441         
13442     
13443         
13444     },
13445
13446     /**
13447      * Allow or prevent the user from directly editing the field text.  If false is passed,
13448      * the user will only be able to select from the items defined in the dropdown list.  This method
13449      * is the runtime equivalent of setting the 'editable' config option at config time.
13450      * @param {Boolean} value True to allow the user to directly edit the field text
13451      */
13452     setEditable : function(value){
13453         if(value == this.editable){
13454             return;
13455         }
13456         this.editable = value;
13457         if(!value){
13458             this.inputEl().dom.setAttribute('readOnly', true);
13459             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13460             this.inputEl().addClass('x-combo-noedit');
13461         }else{
13462             this.inputEl().dom.setAttribute('readOnly', false);
13463             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13464             this.inputEl().removeClass('x-combo-noedit');
13465         }
13466     },
13467
13468     // private
13469     
13470     onBeforeLoad : function(combo,opts){
13471         if(!this.hasFocus){
13472             return;
13473         }
13474          if (!opts.add) {
13475             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13476          }
13477         this.restrictHeight();
13478         this.selectedIndex = -1;
13479     },
13480
13481     // private
13482     onLoad : function(){
13483         
13484         this.hasQuery = false;
13485         
13486         if(!this.hasFocus){
13487             return;
13488         }
13489         
13490         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13491             this.loading.hide();
13492         }
13493         
13494         if(this.store.getCount() > 0){
13495             
13496             this.expand();
13497             this.restrictHeight();
13498             if(this.lastQuery == this.allQuery){
13499                 if(this.editable && !this.tickable){
13500                     this.inputEl().dom.select();
13501                 }
13502                 
13503                 if(
13504                     !this.selectByValue(this.value, true) &&
13505                     this.autoFocus && 
13506                     (
13507                         !this.store.lastOptions ||
13508                         typeof(this.store.lastOptions.add) == 'undefined' || 
13509                         this.store.lastOptions.add != true
13510                     )
13511                 ){
13512                     this.select(0, true);
13513                 }
13514             }else{
13515                 if(this.autoFocus){
13516                     this.selectNext();
13517                 }
13518                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13519                     this.taTask.delay(this.typeAheadDelay);
13520                 }
13521             }
13522         }else{
13523             this.onEmptyResults();
13524         }
13525         
13526         //this.el.focus();
13527     },
13528     // private
13529     onLoadException : function()
13530     {
13531         this.hasQuery = false;
13532         
13533         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13534             this.loading.hide();
13535         }
13536         
13537         if(this.tickable && this.editable){
13538             return;
13539         }
13540         
13541         this.collapse();
13542         // only causes errors at present
13543         //Roo.log(this.store.reader.jsonData);
13544         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13545             // fixme
13546             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13547         //}
13548         
13549         
13550     },
13551     // private
13552     onTypeAhead : function(){
13553         if(this.store.getCount() > 0){
13554             var r = this.store.getAt(0);
13555             var newValue = r.data[this.displayField];
13556             var len = newValue.length;
13557             var selStart = this.getRawValue().length;
13558             
13559             if(selStart != len){
13560                 this.setRawValue(newValue);
13561                 this.selectText(selStart, newValue.length);
13562             }
13563         }
13564     },
13565
13566     // private
13567     onSelect : function(record, index){
13568         
13569         if(this.fireEvent('beforeselect', this, record, index) !== false){
13570         
13571             this.setFromData(index > -1 ? record.data : false);
13572             
13573             this.collapse();
13574             this.fireEvent('select', this, record, index);
13575         }
13576     },
13577
13578     /**
13579      * Returns the currently selected field value or empty string if no value is set.
13580      * @return {String} value The selected value
13581      */
13582     getValue : function()
13583     {
13584         if(Roo.isIOS && this.useNativeIOS){
13585             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13586         }
13587         
13588         if(this.multiple){
13589             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13590         }
13591         
13592         if(this.valueField){
13593             return typeof this.value != 'undefined' ? this.value : '';
13594         }else{
13595             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13596         }
13597     },
13598     
13599     getRawValue : function()
13600     {
13601         if(Roo.isIOS && this.useNativeIOS){
13602             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13603         }
13604         
13605         var v = this.inputEl().getValue();
13606         
13607         return v;
13608     },
13609
13610     /**
13611      * Clears any text/value currently set in the field
13612      */
13613     clearValue : function(){
13614         
13615         if(this.hiddenField){
13616             this.hiddenField.dom.value = '';
13617         }
13618         this.value = '';
13619         this.setRawValue('');
13620         this.lastSelectionText = '';
13621         this.lastData = false;
13622         
13623         var close = this.closeTriggerEl();
13624         
13625         if(close){
13626             close.hide();
13627         }
13628         
13629         this.validate();
13630         
13631     },
13632
13633     /**
13634      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13635      * will be displayed in the field.  If the value does not match the data value of an existing item,
13636      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13637      * Otherwise the field will be blank (although the value will still be set).
13638      * @param {String} value The value to match
13639      */
13640     setValue : function(v)
13641     {
13642         if(Roo.isIOS && this.useNativeIOS){
13643             this.setIOSValue(v);
13644             return;
13645         }
13646         
13647         if(this.multiple){
13648             this.syncValue();
13649             return;
13650         }
13651         
13652         var text = v;
13653         if(this.valueField){
13654             var r = this.findRecord(this.valueField, v);
13655             if(r){
13656                 text = r.data[this.displayField];
13657             }else if(this.valueNotFoundText !== undefined){
13658                 text = this.valueNotFoundText;
13659             }
13660         }
13661         this.lastSelectionText = text;
13662         if(this.hiddenField){
13663             this.hiddenField.dom.value = v;
13664         }
13665         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13666         this.value = v;
13667         
13668         var close = this.closeTriggerEl();
13669         
13670         if(close){
13671             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13672         }
13673         
13674         this.validate();
13675     },
13676     /**
13677      * @property {Object} the last set data for the element
13678      */
13679     
13680     lastData : false,
13681     /**
13682      * Sets the value of the field based on a object which is related to the record format for the store.
13683      * @param {Object} value the value to set as. or false on reset?
13684      */
13685     setFromData : function(o){
13686         
13687         if(this.multiple){
13688             this.addItem(o);
13689             return;
13690         }
13691             
13692         var dv = ''; // display value
13693         var vv = ''; // value value..
13694         this.lastData = o;
13695         if (this.displayField) {
13696             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13697         } else {
13698             // this is an error condition!!!
13699             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13700         }
13701         
13702         if(this.valueField){
13703             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13704         }
13705         
13706         var close = this.closeTriggerEl();
13707         
13708         if(close){
13709             if(dv.length || vv * 1 > 0){
13710                 close.show() ;
13711                 this.blockFocus=true;
13712             } else {
13713                 close.hide();
13714             }             
13715         }
13716         
13717         if(this.hiddenField){
13718             this.hiddenField.dom.value = vv;
13719             
13720             this.lastSelectionText = dv;
13721             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13722             this.value = vv;
13723             return;
13724         }
13725         // no hidden field.. - we store the value in 'value', but still display
13726         // display field!!!!
13727         this.lastSelectionText = dv;
13728         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13729         this.value = vv;
13730         
13731         
13732         
13733     },
13734     // private
13735     reset : function(){
13736         // overridden so that last data is reset..
13737         
13738         if(this.multiple){
13739             this.clearItem();
13740             return;
13741         }
13742         
13743         this.setValue(this.originalValue);
13744         //this.clearInvalid();
13745         this.lastData = false;
13746         if (this.view) {
13747             this.view.clearSelections();
13748         }
13749         
13750         this.validate();
13751     },
13752     // private
13753     findRecord : function(prop, value){
13754         var record;
13755         if(this.store.getCount() > 0){
13756             this.store.each(function(r){
13757                 if(r.data[prop] == value){
13758                     record = r;
13759                     return false;
13760                 }
13761                 return true;
13762             });
13763         }
13764         return record;
13765     },
13766     
13767     getName: function()
13768     {
13769         // returns hidden if it's set..
13770         if (!this.rendered) {return ''};
13771         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13772         
13773     },
13774     // private
13775     onViewMove : function(e, t){
13776         this.inKeyMode = false;
13777     },
13778
13779     // private
13780     onViewOver : function(e, t){
13781         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13782             return;
13783         }
13784         var item = this.view.findItemFromChild(t);
13785         
13786         if(item){
13787             var index = this.view.indexOf(item);
13788             this.select(index, false);
13789         }
13790     },
13791
13792     // private
13793     onViewClick : function(view, doFocus, el, e)
13794     {
13795         var index = this.view.getSelectedIndexes()[0];
13796         
13797         var r = this.store.getAt(index);
13798         
13799         if(this.tickable){
13800             
13801             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13802                 return;
13803             }
13804             
13805             var rm = false;
13806             var _this = this;
13807             
13808             Roo.each(this.tickItems, function(v,k){
13809                 
13810                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13811                     Roo.log(v);
13812                     _this.tickItems.splice(k, 1);
13813                     
13814                     if(typeof(e) == 'undefined' && view == false){
13815                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13816                     }
13817                     
13818                     rm = true;
13819                     return;
13820                 }
13821             });
13822             
13823             if(rm){
13824                 return;
13825             }
13826             
13827             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13828                 this.tickItems.push(r.data);
13829             }
13830             
13831             if(typeof(e) == 'undefined' && view == false){
13832                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13833             }
13834                     
13835             return;
13836         }
13837         
13838         if(r){
13839             this.onSelect(r, index);
13840         }
13841         if(doFocus !== false && !this.blockFocus){
13842             this.inputEl().focus();
13843         }
13844     },
13845
13846     // private
13847     restrictHeight : function(){
13848         //this.innerList.dom.style.height = '';
13849         //var inner = this.innerList.dom;
13850         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13851         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13852         //this.list.beginUpdate();
13853         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13854         this.list.alignTo(this.inputEl(), this.listAlign);
13855         this.list.alignTo(this.inputEl(), this.listAlign);
13856         //this.list.endUpdate();
13857     },
13858
13859     // private
13860     onEmptyResults : function(){
13861         
13862         if(this.tickable && this.editable){
13863             this.restrictHeight();
13864             return;
13865         }
13866         
13867         this.collapse();
13868     },
13869
13870     /**
13871      * Returns true if the dropdown list is expanded, else false.
13872      */
13873     isExpanded : function(){
13874         return this.list.isVisible();
13875     },
13876
13877     /**
13878      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13879      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13880      * @param {String} value The data value of the item to select
13881      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13882      * selected item if it is not currently in view (defaults to true)
13883      * @return {Boolean} True if the value matched an item in the list, else false
13884      */
13885     selectByValue : function(v, scrollIntoView){
13886         if(v !== undefined && v !== null){
13887             var r = this.findRecord(this.valueField || this.displayField, v);
13888             if(r){
13889                 this.select(this.store.indexOf(r), scrollIntoView);
13890                 return true;
13891             }
13892         }
13893         return false;
13894     },
13895
13896     /**
13897      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13898      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13899      * @param {Number} index The zero-based index of the list item to select
13900      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13901      * selected item if it is not currently in view (defaults to true)
13902      */
13903     select : function(index, scrollIntoView){
13904         this.selectedIndex = index;
13905         this.view.select(index);
13906         if(scrollIntoView !== false){
13907             var el = this.view.getNode(index);
13908             /*
13909              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13910              */
13911             if(el){
13912                 this.list.scrollChildIntoView(el, false);
13913             }
13914         }
13915     },
13916
13917     // private
13918     selectNext : function(){
13919         var ct = this.store.getCount();
13920         if(ct > 0){
13921             if(this.selectedIndex == -1){
13922                 this.select(0);
13923             }else if(this.selectedIndex < ct-1){
13924                 this.select(this.selectedIndex+1);
13925             }
13926         }
13927     },
13928
13929     // private
13930     selectPrev : function(){
13931         var ct = this.store.getCount();
13932         if(ct > 0){
13933             if(this.selectedIndex == -1){
13934                 this.select(0);
13935             }else if(this.selectedIndex != 0){
13936                 this.select(this.selectedIndex-1);
13937             }
13938         }
13939     },
13940
13941     // private
13942     onKeyUp : function(e){
13943         if(this.editable !== false && !e.isSpecialKey()){
13944             this.lastKey = e.getKey();
13945             this.dqTask.delay(this.queryDelay);
13946         }
13947     },
13948
13949     // private
13950     validateBlur : function(){
13951         return !this.list || !this.list.isVisible();   
13952     },
13953
13954     // private
13955     initQuery : function(){
13956         
13957         var v = this.getRawValue();
13958         
13959         if(this.tickable && this.editable){
13960             v = this.tickableInputEl().getValue();
13961         }
13962         
13963         this.doQuery(v);
13964     },
13965
13966     // private
13967     doForce : function(){
13968         if(this.inputEl().dom.value.length > 0){
13969             this.inputEl().dom.value =
13970                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13971              
13972         }
13973     },
13974
13975     /**
13976      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13977      * query allowing the query action to be canceled if needed.
13978      * @param {String} query The SQL query to execute
13979      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13980      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13981      * saved in the current store (defaults to false)
13982      */
13983     doQuery : function(q, forceAll){
13984         
13985         if(q === undefined || q === null){
13986             q = '';
13987         }
13988         var qe = {
13989             query: q,
13990             forceAll: forceAll,
13991             combo: this,
13992             cancel:false
13993         };
13994         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13995             return false;
13996         }
13997         q = qe.query;
13998         
13999         forceAll = qe.forceAll;
14000         if(forceAll === true || (q.length >= this.minChars)){
14001             
14002             this.hasQuery = true;
14003             
14004             if(this.lastQuery != q || this.alwaysQuery){
14005                 this.lastQuery = q;
14006                 if(this.mode == 'local'){
14007                     this.selectedIndex = -1;
14008                     if(forceAll){
14009                         this.store.clearFilter();
14010                     }else{
14011                         
14012                         if(this.specialFilter){
14013                             this.fireEvent('specialfilter', this);
14014                             this.onLoad();
14015                             return;
14016                         }
14017                         
14018                         this.store.filter(this.displayField, q);
14019                     }
14020                     
14021                     this.store.fireEvent("datachanged", this.store);
14022                     
14023                     this.onLoad();
14024                     
14025                     
14026                 }else{
14027                     
14028                     this.store.baseParams[this.queryParam] = q;
14029                     
14030                     var options = {params : this.getParams(q)};
14031                     
14032                     if(this.loadNext){
14033                         options.add = true;
14034                         options.params.start = this.page * this.pageSize;
14035                     }
14036                     
14037                     this.store.load(options);
14038                     
14039                     /*
14040                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14041                      *  we should expand the list on onLoad
14042                      *  so command out it
14043                      */
14044 //                    this.expand();
14045                 }
14046             }else{
14047                 this.selectedIndex = -1;
14048                 this.onLoad();   
14049             }
14050         }
14051         
14052         this.loadNext = false;
14053     },
14054     
14055     // private
14056     getParams : function(q){
14057         var p = {};
14058         //p[this.queryParam] = q;
14059         
14060         if(this.pageSize){
14061             p.start = 0;
14062             p.limit = this.pageSize;
14063         }
14064         return p;
14065     },
14066
14067     /**
14068      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14069      */
14070     collapse : function(){
14071         if(!this.isExpanded()){
14072             return;
14073         }
14074         
14075         this.list.hide();
14076         
14077         this.hasFocus = false;
14078         
14079         if(this.tickable){
14080             this.okBtn.hide();
14081             this.cancelBtn.hide();
14082             this.trigger.show();
14083             
14084             if(this.editable){
14085                 this.tickableInputEl().dom.value = '';
14086                 this.tickableInputEl().blur();
14087             }
14088             
14089         }
14090         
14091         Roo.get(document).un('mousedown', this.collapseIf, this);
14092         Roo.get(document).un('mousewheel', this.collapseIf, this);
14093         if (!this.editable) {
14094             Roo.get(document).un('keydown', this.listKeyPress, this);
14095         }
14096         this.fireEvent('collapse', this);
14097         
14098         this.validate();
14099     },
14100
14101     // private
14102     collapseIf : function(e){
14103         var in_combo  = e.within(this.el);
14104         var in_list =  e.within(this.list);
14105         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14106         
14107         if (in_combo || in_list || is_list) {
14108             //e.stopPropagation();
14109             return;
14110         }
14111         
14112         if(this.tickable){
14113             this.onTickableFooterButtonClick(e, false, false);
14114         }
14115
14116         this.collapse();
14117         
14118     },
14119
14120     /**
14121      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14122      */
14123     expand : function(){
14124        
14125         if(this.isExpanded() || !this.hasFocus){
14126             return;
14127         }
14128         
14129         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14130         this.list.setWidth(lw);
14131         
14132         Roo.log('expand');
14133         
14134         this.list.show();
14135         
14136         this.restrictHeight();
14137         
14138         if(this.tickable){
14139             
14140             this.tickItems = Roo.apply([], this.item);
14141             
14142             this.okBtn.show();
14143             this.cancelBtn.show();
14144             this.trigger.hide();
14145             
14146             if(this.editable){
14147                 this.tickableInputEl().focus();
14148             }
14149             
14150         }
14151         
14152         Roo.get(document).on('mousedown', this.collapseIf, this);
14153         Roo.get(document).on('mousewheel', this.collapseIf, this);
14154         if (!this.editable) {
14155             Roo.get(document).on('keydown', this.listKeyPress, this);
14156         }
14157         
14158         this.fireEvent('expand', this);
14159     },
14160
14161     // private
14162     // Implements the default empty TriggerField.onTriggerClick function
14163     onTriggerClick : function(e)
14164     {
14165         Roo.log('trigger click');
14166         
14167         if(this.disabled || !this.triggerList){
14168             return;
14169         }
14170         
14171         this.page = 0;
14172         this.loadNext = false;
14173         
14174         if(this.isExpanded()){
14175             this.collapse();
14176             if (!this.blockFocus) {
14177                 this.inputEl().focus();
14178             }
14179             
14180         }else {
14181             this.hasFocus = true;
14182             if(this.triggerAction == 'all') {
14183                 this.doQuery(this.allQuery, true);
14184             } else {
14185                 this.doQuery(this.getRawValue());
14186             }
14187             if (!this.blockFocus) {
14188                 this.inputEl().focus();
14189             }
14190         }
14191     },
14192     
14193     onTickableTriggerClick : function(e)
14194     {
14195         if(this.disabled){
14196             return;
14197         }
14198         
14199         this.page = 0;
14200         this.loadNext = false;
14201         this.hasFocus = true;
14202         
14203         if(this.triggerAction == 'all') {
14204             this.doQuery(this.allQuery, true);
14205         } else {
14206             this.doQuery(this.getRawValue());
14207         }
14208     },
14209     
14210     onSearchFieldClick : function(e)
14211     {
14212         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14213             this.onTickableFooterButtonClick(e, false, false);
14214             return;
14215         }
14216         
14217         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14218             return;
14219         }
14220         
14221         this.page = 0;
14222         this.loadNext = false;
14223         this.hasFocus = true;
14224         
14225         if(this.triggerAction == 'all') {
14226             this.doQuery(this.allQuery, true);
14227         } else {
14228             this.doQuery(this.getRawValue());
14229         }
14230     },
14231     
14232     listKeyPress : function(e)
14233     {
14234         //Roo.log('listkeypress');
14235         // scroll to first matching element based on key pres..
14236         if (e.isSpecialKey()) {
14237             return false;
14238         }
14239         var k = String.fromCharCode(e.getKey()).toUpperCase();
14240         //Roo.log(k);
14241         var match  = false;
14242         var csel = this.view.getSelectedNodes();
14243         var cselitem = false;
14244         if (csel.length) {
14245             var ix = this.view.indexOf(csel[0]);
14246             cselitem  = this.store.getAt(ix);
14247             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14248                 cselitem = false;
14249             }
14250             
14251         }
14252         
14253         this.store.each(function(v) { 
14254             if (cselitem) {
14255                 // start at existing selection.
14256                 if (cselitem.id == v.id) {
14257                     cselitem = false;
14258                 }
14259                 return true;
14260             }
14261                 
14262             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14263                 match = this.store.indexOf(v);
14264                 return false;
14265             }
14266             return true;
14267         }, this);
14268         
14269         if (match === false) {
14270             return true; // no more action?
14271         }
14272         // scroll to?
14273         this.view.select(match);
14274         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14275         sn.scrollIntoView(sn.dom.parentNode, false);
14276     },
14277     
14278     onViewScroll : function(e, t){
14279         
14280         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){
14281             return;
14282         }
14283         
14284         this.hasQuery = true;
14285         
14286         this.loading = this.list.select('.loading', true).first();
14287         
14288         if(this.loading === null){
14289             this.list.createChild({
14290                 tag: 'div',
14291                 cls: 'loading roo-select2-more-results roo-select2-active',
14292                 html: 'Loading more results...'
14293             });
14294             
14295             this.loading = this.list.select('.loading', true).first();
14296             
14297             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14298             
14299             this.loading.hide();
14300         }
14301         
14302         this.loading.show();
14303         
14304         var _combo = this;
14305         
14306         this.page++;
14307         this.loadNext = true;
14308         
14309         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14310         
14311         return;
14312     },
14313     
14314     addItem : function(o)
14315     {   
14316         var dv = ''; // display value
14317         
14318         if (this.displayField) {
14319             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14320         } else {
14321             // this is an error condition!!!
14322             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14323         }
14324         
14325         if(!dv.length){
14326             return;
14327         }
14328         
14329         var choice = this.choices.createChild({
14330             tag: 'li',
14331             cls: 'roo-select2-search-choice',
14332             cn: [
14333                 {
14334                     tag: 'div',
14335                     html: dv
14336                 },
14337                 {
14338                     tag: 'a',
14339                     href: '#',
14340                     cls: 'roo-select2-search-choice-close fa fa-times',
14341                     tabindex: '-1'
14342                 }
14343             ]
14344             
14345         }, this.searchField);
14346         
14347         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14348         
14349         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14350         
14351         this.item.push(o);
14352         
14353         this.lastData = o;
14354         
14355         this.syncValue();
14356         
14357         this.inputEl().dom.value = '';
14358         
14359         this.validate();
14360     },
14361     
14362     onRemoveItem : function(e, _self, o)
14363     {
14364         e.preventDefault();
14365         
14366         this.lastItem = Roo.apply([], this.item);
14367         
14368         var index = this.item.indexOf(o.data) * 1;
14369         
14370         if( index < 0){
14371             Roo.log('not this item?!');
14372             return;
14373         }
14374         
14375         this.item.splice(index, 1);
14376         o.item.remove();
14377         
14378         this.syncValue();
14379         
14380         this.fireEvent('remove', this, e);
14381         
14382         this.validate();
14383         
14384     },
14385     
14386     syncValue : function()
14387     {
14388         if(!this.item.length){
14389             this.clearValue();
14390             return;
14391         }
14392             
14393         var value = [];
14394         var _this = this;
14395         Roo.each(this.item, function(i){
14396             if(_this.valueField){
14397                 value.push(i[_this.valueField]);
14398                 return;
14399             }
14400
14401             value.push(i);
14402         });
14403
14404         this.value = value.join(',');
14405
14406         if(this.hiddenField){
14407             this.hiddenField.dom.value = this.value;
14408         }
14409         
14410         this.store.fireEvent("datachanged", this.store);
14411         
14412         this.validate();
14413     },
14414     
14415     clearItem : function()
14416     {
14417         if(!this.multiple){
14418             return;
14419         }
14420         
14421         this.item = [];
14422         
14423         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14424            c.remove();
14425         });
14426         
14427         this.syncValue();
14428         
14429         this.validate();
14430         
14431         if(this.tickable && !Roo.isTouch){
14432             this.view.refresh();
14433         }
14434     },
14435     
14436     inputEl: function ()
14437     {
14438         if(Roo.isIOS && this.useNativeIOS){
14439             return this.el.select('select.roo-ios-select', true).first();
14440         }
14441         
14442         if(Roo.isTouch && this.mobileTouchView){
14443             return this.el.select('input.form-control',true).first();
14444         }
14445         
14446         if(this.tickable){
14447             return this.searchField;
14448         }
14449         
14450         return this.el.select('input.form-control',true).first();
14451     },
14452     
14453     onTickableFooterButtonClick : function(e, btn, el)
14454     {
14455         e.preventDefault();
14456         
14457         this.lastItem = Roo.apply([], this.item);
14458         
14459         if(btn && btn.name == 'cancel'){
14460             this.tickItems = Roo.apply([], this.item);
14461             this.collapse();
14462             return;
14463         }
14464         
14465         this.clearItem();
14466         
14467         var _this = this;
14468         
14469         Roo.each(this.tickItems, function(o){
14470             _this.addItem(o);
14471         });
14472         
14473         this.collapse();
14474         
14475     },
14476     
14477     validate : function()
14478     {
14479         var v = this.getRawValue();
14480         
14481         if(this.multiple){
14482             v = this.getValue();
14483         }
14484         
14485         if(this.disabled || this.allowBlank || v.length){
14486             this.markValid();
14487             return true;
14488         }
14489         
14490         this.markInvalid();
14491         return false;
14492     },
14493     
14494     tickableInputEl : function()
14495     {
14496         if(!this.tickable || !this.editable){
14497             return this.inputEl();
14498         }
14499         
14500         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14501     },
14502     
14503     
14504     getAutoCreateTouchView : function()
14505     {
14506         var id = Roo.id();
14507         
14508         var cfg = {
14509             cls: 'form-group' //input-group
14510         };
14511         
14512         var input =  {
14513             tag: 'input',
14514             id : id,
14515             type : this.inputType,
14516             cls : 'form-control x-combo-noedit',
14517             autocomplete: 'new-password',
14518             placeholder : this.placeholder || '',
14519             readonly : true
14520         };
14521         
14522         if (this.name) {
14523             input.name = this.name;
14524         }
14525         
14526         if (this.size) {
14527             input.cls += ' input-' + this.size;
14528         }
14529         
14530         if (this.disabled) {
14531             input.disabled = true;
14532         }
14533         
14534         var inputblock = {
14535             cls : '',
14536             cn : [
14537                 input
14538             ]
14539         };
14540         
14541         if(this.before){
14542             inputblock.cls += ' input-group';
14543             
14544             inputblock.cn.unshift({
14545                 tag :'span',
14546                 cls : 'input-group-addon',
14547                 html : this.before
14548             });
14549         }
14550         
14551         if(this.removable && !this.multiple){
14552             inputblock.cls += ' roo-removable';
14553             
14554             inputblock.cn.push({
14555                 tag: 'button',
14556                 html : 'x',
14557                 cls : 'roo-combo-removable-btn close'
14558             });
14559         }
14560
14561         if(this.hasFeedback && !this.allowBlank){
14562             
14563             inputblock.cls += ' has-feedback';
14564             
14565             inputblock.cn.push({
14566                 tag: 'span',
14567                 cls: 'glyphicon form-control-feedback'
14568             });
14569             
14570         }
14571         
14572         if (this.after) {
14573             
14574             inputblock.cls += (this.before) ? '' : ' input-group';
14575             
14576             inputblock.cn.push({
14577                 tag :'span',
14578                 cls : 'input-group-addon',
14579                 html : this.after
14580             });
14581         }
14582
14583         var box = {
14584             tag: 'div',
14585             cn: [
14586                 {
14587                     tag: 'input',
14588                     type : 'hidden',
14589                     cls: 'form-hidden-field'
14590                 },
14591                 inputblock
14592             ]
14593             
14594         };
14595         
14596         if(this.multiple){
14597             box = {
14598                 tag: 'div',
14599                 cn: [
14600                     {
14601                         tag: 'input',
14602                         type : 'hidden',
14603                         cls: 'form-hidden-field'
14604                     },
14605                     {
14606                         tag: 'ul',
14607                         cls: 'roo-select2-choices',
14608                         cn:[
14609                             {
14610                                 tag: 'li',
14611                                 cls: 'roo-select2-search-field',
14612                                 cn: [
14613
14614                                     inputblock
14615                                 ]
14616                             }
14617                         ]
14618                     }
14619                 ]
14620             }
14621         };
14622         
14623         var combobox = {
14624             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14625             cn: [
14626                 box
14627             ]
14628         };
14629         
14630         if(!this.multiple && this.showToggleBtn){
14631             
14632             var caret = {
14633                         tag: 'span',
14634                         cls: 'caret'
14635             };
14636             
14637             if (this.caret != false) {
14638                 caret = {
14639                      tag: 'i',
14640                      cls: 'fa fa-' + this.caret
14641                 };
14642                 
14643             }
14644             
14645             combobox.cn.push({
14646                 tag :'span',
14647                 cls : 'input-group-addon btn dropdown-toggle',
14648                 cn : [
14649                     caret,
14650                     {
14651                         tag: 'span',
14652                         cls: 'combobox-clear',
14653                         cn  : [
14654                             {
14655                                 tag : 'i',
14656                                 cls: 'icon-remove'
14657                             }
14658                         ]
14659                     }
14660                 ]
14661
14662             })
14663         }
14664         
14665         if(this.multiple){
14666             combobox.cls += ' roo-select2-container-multi';
14667         }
14668         
14669         var align = this.labelAlign || this.parentLabelAlign();
14670         
14671         if (align ==='left' && this.fieldLabel.length) {
14672
14673             cfg.cn = [
14674                 {
14675                    tag : 'i',
14676                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14677                    tooltip : 'This field is required'
14678                 },
14679                 {
14680                     tag: 'label',
14681                     cls : 'control-label',
14682                     html : this.fieldLabel
14683
14684                 },
14685                 {
14686                     cls : '', 
14687                     cn: [
14688                         combobox
14689                     ]
14690                 }
14691             ];
14692             
14693             var labelCfg = cfg.cn[1];
14694             var contentCfg = cfg.cn[2];
14695             
14696
14697             if(this.indicatorpos == 'right'){
14698                 cfg.cn = [
14699                     {
14700                         tag: 'label',
14701                         cls : 'control-label',
14702                         html : this.fieldLabel,
14703                         cn : [
14704                             {
14705                                tag : 'i',
14706                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14707                                tooltip : 'This field is required'
14708                             }
14709                         ]
14710                     },
14711                     {
14712                         cls : '', 
14713                         cn: [
14714                             combobox
14715                         ]
14716                     }
14717                 ];
14718                 labelCfg = cfg.cn[0];
14719                 contentCfg = cfg.cn[1];
14720             }
14721             
14722            
14723             
14724             if(this.labelWidth > 12){
14725                 labelCfg.style = "width: " + this.labelWidth + 'px';
14726             }
14727             
14728             if(this.labelWidth < 13 && this.labelmd == 0){
14729                 this.labelmd = this.labelWidth;
14730             }
14731             
14732             if(this.labellg > 0){
14733                 labelCfg.cls += ' col-lg-' + this.labellg;
14734                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14735             }
14736             
14737             if(this.labelmd > 0){
14738                 labelCfg.cls += ' col-md-' + this.labelmd;
14739                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14740             }
14741             
14742             if(this.labelsm > 0){
14743                 labelCfg.cls += ' col-sm-' + this.labelsm;
14744                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14745             }
14746             
14747             if(this.labelxs > 0){
14748                 labelCfg.cls += ' col-xs-' + this.labelxs;
14749                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14750             }
14751                 
14752                 
14753         } else if ( this.fieldLabel.length) {
14754             cfg.cn = [
14755                 {
14756                    tag : 'i',
14757                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14758                    tooltip : 'This field is required'
14759                 },
14760                 {
14761                     tag: 'label',
14762                     cls : 'control-label',
14763                     html : this.fieldLabel
14764
14765                 },
14766                 {
14767                     cls : '', 
14768                     cn: [
14769                         combobox
14770                     ]
14771                 }
14772             ];
14773             
14774             if(this.indicatorpos == 'right'){
14775                 cfg.cn = [
14776                     {
14777                         tag: 'label',
14778                         cls : 'control-label',
14779                         html : this.fieldLabel,
14780                         cn : [
14781                             {
14782                                tag : 'i',
14783                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14784                                tooltip : 'This field is required'
14785                             }
14786                         ]
14787                     },
14788                     {
14789                         cls : '', 
14790                         cn: [
14791                             combobox
14792                         ]
14793                     }
14794                 ];
14795             }
14796         } else {
14797             cfg.cn = combobox;    
14798         }
14799         
14800         
14801         var settings = this;
14802         
14803         ['xs','sm','md','lg'].map(function(size){
14804             if (settings[size]) {
14805                 cfg.cls += ' col-' + size + '-' + settings[size];
14806             }
14807         });
14808         
14809         return cfg;
14810     },
14811     
14812     initTouchView : function()
14813     {
14814         this.renderTouchView();
14815         
14816         this.touchViewEl.on('scroll', function(){
14817             this.el.dom.scrollTop = 0;
14818         }, this);
14819         
14820         this.originalValue = this.getValue();
14821         
14822         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14823         
14824         this.inputEl().on("click", this.showTouchView, this);
14825         if (this.triggerEl) {
14826             this.triggerEl.on("click", this.showTouchView, this);
14827         }
14828         
14829         
14830         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14831         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14832         
14833         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14834         
14835         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14836         this.store.on('load', this.onTouchViewLoad, this);
14837         this.store.on('loadexception', this.onTouchViewLoadException, this);
14838         
14839         if(this.hiddenName){
14840             
14841             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14842             
14843             this.hiddenField.dom.value =
14844                 this.hiddenValue !== undefined ? this.hiddenValue :
14845                 this.value !== undefined ? this.value : '';
14846         
14847             this.el.dom.removeAttribute('name');
14848             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14849         }
14850         
14851         if(this.multiple){
14852             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14853             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14854         }
14855         
14856         if(this.removable && !this.multiple){
14857             var close = this.closeTriggerEl();
14858             if(close){
14859                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14860                 close.on('click', this.removeBtnClick, this, close);
14861             }
14862         }
14863         /*
14864          * fix the bug in Safari iOS8
14865          */
14866         this.inputEl().on("focus", function(e){
14867             document.activeElement.blur();
14868         }, this);
14869         
14870         return;
14871         
14872         
14873     },
14874     
14875     renderTouchView : function()
14876     {
14877         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14878         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14879         
14880         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14881         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14882         
14883         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14884         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14885         this.touchViewBodyEl.setStyle('overflow', 'auto');
14886         
14887         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14888         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14889         
14890         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14891         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14892         
14893     },
14894     
14895     showTouchView : function()
14896     {
14897         if(this.disabled){
14898             return;
14899         }
14900         
14901         this.touchViewHeaderEl.hide();
14902
14903         if(this.modalTitle.length){
14904             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14905             this.touchViewHeaderEl.show();
14906         }
14907
14908         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14909         this.touchViewEl.show();
14910
14911         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14912         
14913         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14914         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14915
14916         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14917
14918         if(this.modalTitle.length){
14919             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14920         }
14921         
14922         this.touchViewBodyEl.setHeight(bodyHeight);
14923
14924         if(this.animate){
14925             var _this = this;
14926             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14927         }else{
14928             this.touchViewEl.addClass('in');
14929         }
14930
14931         this.doTouchViewQuery();
14932         
14933     },
14934     
14935     hideTouchView : function()
14936     {
14937         this.touchViewEl.removeClass('in');
14938
14939         if(this.animate){
14940             var _this = this;
14941             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14942         }else{
14943             this.touchViewEl.setStyle('display', 'none');
14944         }
14945         
14946     },
14947     
14948     setTouchViewValue : function()
14949     {
14950         if(this.multiple){
14951             this.clearItem();
14952         
14953             var _this = this;
14954
14955             Roo.each(this.tickItems, function(o){
14956                 this.addItem(o);
14957             }, this);
14958         }
14959         
14960         this.hideTouchView();
14961     },
14962     
14963     doTouchViewQuery : function()
14964     {
14965         var qe = {
14966             query: '',
14967             forceAll: true,
14968             combo: this,
14969             cancel:false
14970         };
14971         
14972         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14973             return false;
14974         }
14975         
14976         if(!this.alwaysQuery || this.mode == 'local'){
14977             this.onTouchViewLoad();
14978             return;
14979         }
14980         
14981         this.store.load();
14982     },
14983     
14984     onTouchViewBeforeLoad : function(combo,opts)
14985     {
14986         return;
14987     },
14988
14989     // private
14990     onTouchViewLoad : function()
14991     {
14992         if(this.store.getCount() < 1){
14993             this.onTouchViewEmptyResults();
14994             return;
14995         }
14996         
14997         this.clearTouchView();
14998         
14999         var rawValue = this.getRawValue();
15000         
15001         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15002         
15003         this.tickItems = [];
15004         
15005         this.store.data.each(function(d, rowIndex){
15006             var row = this.touchViewListGroup.createChild(template);
15007             
15008             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15009                 row.addClass(d.data.cls);
15010             }
15011             
15012             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15013                 var cfg = {
15014                     data : d.data,
15015                     html : d.data[this.displayField]
15016                 };
15017                 
15018                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15019                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15020                 }
15021             }
15022             row.removeClass('selected');
15023             if(!this.multiple && this.valueField &&
15024                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15025             {
15026                 // radio buttons..
15027                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15028                 row.addClass('selected');
15029             }
15030             
15031             if(this.multiple && this.valueField &&
15032                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15033             {
15034                 
15035                 // checkboxes...
15036                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15037                 this.tickItems.push(d.data);
15038             }
15039             
15040             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15041             
15042         }, this);
15043         
15044         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15045         
15046         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15047
15048         if(this.modalTitle.length){
15049             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15050         }
15051
15052         var listHeight = this.touchViewListGroup.getHeight();
15053         
15054         var _this = this;
15055         
15056         if(firstChecked && listHeight > bodyHeight){
15057             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15058         }
15059         
15060     },
15061     
15062     onTouchViewLoadException : function()
15063     {
15064         this.hideTouchView();
15065     },
15066     
15067     onTouchViewEmptyResults : function()
15068     {
15069         this.clearTouchView();
15070         
15071         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15072         
15073         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15074         
15075     },
15076     
15077     clearTouchView : function()
15078     {
15079         this.touchViewListGroup.dom.innerHTML = '';
15080     },
15081     
15082     onTouchViewClick : function(e, el, o)
15083     {
15084         e.preventDefault();
15085         
15086         var row = o.row;
15087         var rowIndex = o.rowIndex;
15088         
15089         var r = this.store.getAt(rowIndex);
15090         
15091         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15092             
15093             if(!this.multiple){
15094                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15095                     c.dom.removeAttribute('checked');
15096                 }, this);
15097
15098                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15099
15100                 this.setFromData(r.data);
15101
15102                 var close = this.closeTriggerEl();
15103
15104                 if(close){
15105                     close.show();
15106                 }
15107
15108                 this.hideTouchView();
15109
15110                 this.fireEvent('select', this, r, rowIndex);
15111
15112                 return;
15113             }
15114
15115             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15116                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15117                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15118                 return;
15119             }
15120
15121             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15122             this.addItem(r.data);
15123             this.tickItems.push(r.data);
15124         }
15125     },
15126     
15127     getAutoCreateNativeIOS : function()
15128     {
15129         var cfg = {
15130             cls: 'form-group' //input-group,
15131         };
15132         
15133         var combobox =  {
15134             tag: 'select',
15135             cls : 'roo-ios-select'
15136         };
15137         
15138         if (this.name) {
15139             combobox.name = this.name;
15140         }
15141         
15142         if (this.disabled) {
15143             combobox.disabled = true;
15144         }
15145         
15146         var settings = this;
15147         
15148         ['xs','sm','md','lg'].map(function(size){
15149             if (settings[size]) {
15150                 cfg.cls += ' col-' + size + '-' + settings[size];
15151             }
15152         });
15153         
15154         cfg.cn = combobox;
15155         
15156         return cfg;
15157         
15158     },
15159     
15160     initIOSView : function()
15161     {
15162         this.store.on('load', this.onIOSViewLoad, this);
15163         
15164         return;
15165     },
15166     
15167     onIOSViewLoad : function()
15168     {
15169         if(this.store.getCount() < 1){
15170             return;
15171         }
15172         
15173         this.clearIOSView();
15174         
15175         if(this.allowBlank) {
15176             
15177             var default_text = '-- SELECT --';
15178             
15179             var opt = this.inputEl().createChild({
15180                 tag: 'option',
15181                 value : 0,
15182                 html : default_text
15183             });
15184             
15185             var o = {};
15186             o[this.valueField] = 0;
15187             o[this.displayField] = default_text;
15188             
15189             this.ios_options.push({
15190                 data : o,
15191                 el : opt
15192             });
15193             
15194         }
15195         
15196         this.store.data.each(function(d, rowIndex){
15197             
15198             var html = '';
15199             
15200             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15201                 html = d.data[this.displayField];
15202             }
15203             
15204             var value = '';
15205             
15206             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15207                 value = d.data[this.valueField];
15208             }
15209             
15210             var option = {
15211                 tag: 'option',
15212                 value : value,
15213                 html : html
15214             };
15215             
15216             if(this.value == d.data[this.valueField]){
15217                 option['selected'] = true;
15218             }
15219             
15220             var opt = this.inputEl().createChild(option);
15221             
15222             this.ios_options.push({
15223                 data : d.data,
15224                 el : opt
15225             });
15226             
15227         }, this);
15228         
15229         this.inputEl().on('change', function(){
15230            this.fireEvent('select', this);
15231         }, this);
15232         
15233     },
15234     
15235     clearIOSView: function()
15236     {
15237         this.inputEl().dom.innerHTML = '';
15238         
15239         this.ios_options = [];
15240     },
15241     
15242     setIOSValue: function(v)
15243     {
15244         this.value = v;
15245         
15246         if(!this.ios_options){
15247             return;
15248         }
15249         
15250         Roo.each(this.ios_options, function(opts){
15251            
15252            opts.el.dom.removeAttribute('selected');
15253            
15254            if(opts.data[this.valueField] != v){
15255                return;
15256            }
15257            
15258            opts.el.dom.setAttribute('selected', true);
15259            
15260         }, this);
15261     }
15262
15263     /** 
15264     * @cfg {Boolean} grow 
15265     * @hide 
15266     */
15267     /** 
15268     * @cfg {Number} growMin 
15269     * @hide 
15270     */
15271     /** 
15272     * @cfg {Number} growMax 
15273     * @hide 
15274     */
15275     /**
15276      * @hide
15277      * @method autoSize
15278      */
15279 });
15280
15281 Roo.apply(Roo.bootstrap.ComboBox,  {
15282     
15283     header : {
15284         tag: 'div',
15285         cls: 'modal-header',
15286         cn: [
15287             {
15288                 tag: 'h4',
15289                 cls: 'modal-title'
15290             }
15291         ]
15292     },
15293     
15294     body : {
15295         tag: 'div',
15296         cls: 'modal-body',
15297         cn: [
15298             {
15299                 tag: 'ul',
15300                 cls: 'list-group'
15301             }
15302         ]
15303     },
15304     
15305     listItemRadio : {
15306         tag: 'li',
15307         cls: 'list-group-item',
15308         cn: [
15309             {
15310                 tag: 'span',
15311                 cls: 'roo-combobox-list-group-item-value'
15312             },
15313             {
15314                 tag: 'div',
15315                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15316                 cn: [
15317                     {
15318                         tag: 'input',
15319                         type: 'radio'
15320                     },
15321                     {
15322                         tag: 'label'
15323                     }
15324                 ]
15325             }
15326         ]
15327     },
15328     
15329     listItemCheckbox : {
15330         tag: 'li',
15331         cls: 'list-group-item',
15332         cn: [
15333             {
15334                 tag: 'span',
15335                 cls: 'roo-combobox-list-group-item-value'
15336             },
15337             {
15338                 tag: 'div',
15339                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15340                 cn: [
15341                     {
15342                         tag: 'input',
15343                         type: 'checkbox'
15344                     },
15345                     {
15346                         tag: 'label'
15347                     }
15348                 ]
15349             }
15350         ]
15351     },
15352     
15353     emptyResult : {
15354         tag: 'div',
15355         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15356     },
15357     
15358     footer : {
15359         tag: 'div',
15360         cls: 'modal-footer',
15361         cn: [
15362             {
15363                 tag: 'div',
15364                 cls: 'row',
15365                 cn: [
15366                     {
15367                         tag: 'div',
15368                         cls: 'col-xs-6 text-left',
15369                         cn: {
15370                             tag: 'button',
15371                             cls: 'btn btn-danger roo-touch-view-cancel',
15372                             html: 'Cancel'
15373                         }
15374                     },
15375                     {
15376                         tag: 'div',
15377                         cls: 'col-xs-6 text-right',
15378                         cn: {
15379                             tag: 'button',
15380                             cls: 'btn btn-success roo-touch-view-ok',
15381                             html: 'OK'
15382                         }
15383                     }
15384                 ]
15385             }
15386         ]
15387         
15388     }
15389 });
15390
15391 Roo.apply(Roo.bootstrap.ComboBox,  {
15392     
15393     touchViewTemplate : {
15394         tag: 'div',
15395         cls: 'modal fade roo-combobox-touch-view',
15396         cn: [
15397             {
15398                 tag: 'div',
15399                 cls: 'modal-dialog',
15400                 style : 'position:fixed', // we have to fix position....
15401                 cn: [
15402                     {
15403                         tag: 'div',
15404                         cls: 'modal-content',
15405                         cn: [
15406                             Roo.bootstrap.ComboBox.header,
15407                             Roo.bootstrap.ComboBox.body,
15408                             Roo.bootstrap.ComboBox.footer
15409                         ]
15410                     }
15411                 ]
15412             }
15413         ]
15414     }
15415 });/*
15416  * Based on:
15417  * Ext JS Library 1.1.1
15418  * Copyright(c) 2006-2007, Ext JS, LLC.
15419  *
15420  * Originally Released Under LGPL - original licence link has changed is not relivant.
15421  *
15422  * Fork - LGPL
15423  * <script type="text/javascript">
15424  */
15425
15426 /**
15427  * @class Roo.View
15428  * @extends Roo.util.Observable
15429  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15430  * This class also supports single and multi selection modes. <br>
15431  * Create a data model bound view:
15432  <pre><code>
15433  var store = new Roo.data.Store(...);
15434
15435  var view = new Roo.View({
15436     el : "my-element",
15437     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15438  
15439     singleSelect: true,
15440     selectedClass: "ydataview-selected",
15441     store: store
15442  });
15443
15444  // listen for node click?
15445  view.on("click", function(vw, index, node, e){
15446  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15447  });
15448
15449  // load XML data
15450  dataModel.load("foobar.xml");
15451  </code></pre>
15452  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15453  * <br><br>
15454  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15455  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15456  * 
15457  * Note: old style constructor is still suported (container, template, config)
15458  * 
15459  * @constructor
15460  * Create a new View
15461  * @param {Object} config The config object
15462  * 
15463  */
15464 Roo.View = function(config, depreciated_tpl, depreciated_config){
15465     
15466     this.parent = false;
15467     
15468     if (typeof(depreciated_tpl) == 'undefined') {
15469         // new way.. - universal constructor.
15470         Roo.apply(this, config);
15471         this.el  = Roo.get(this.el);
15472     } else {
15473         // old format..
15474         this.el  = Roo.get(config);
15475         this.tpl = depreciated_tpl;
15476         Roo.apply(this, depreciated_config);
15477     }
15478     this.wrapEl  = this.el.wrap().wrap();
15479     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15480     
15481     
15482     if(typeof(this.tpl) == "string"){
15483         this.tpl = new Roo.Template(this.tpl);
15484     } else {
15485         // support xtype ctors..
15486         this.tpl = new Roo.factory(this.tpl, Roo);
15487     }
15488     
15489     
15490     this.tpl.compile();
15491     
15492     /** @private */
15493     this.addEvents({
15494         /**
15495          * @event beforeclick
15496          * Fires before a click is processed. Returns false to cancel the default action.
15497          * @param {Roo.View} this
15498          * @param {Number} index The index of the target node
15499          * @param {HTMLElement} node The target node
15500          * @param {Roo.EventObject} e The raw event object
15501          */
15502             "beforeclick" : true,
15503         /**
15504          * @event click
15505          * Fires when a template node is clicked.
15506          * @param {Roo.View} this
15507          * @param {Number} index The index of the target node
15508          * @param {HTMLElement} node The target node
15509          * @param {Roo.EventObject} e The raw event object
15510          */
15511             "click" : true,
15512         /**
15513          * @event dblclick
15514          * Fires when a template node is double clicked.
15515          * @param {Roo.View} this
15516          * @param {Number} index The index of the target node
15517          * @param {HTMLElement} node The target node
15518          * @param {Roo.EventObject} e The raw event object
15519          */
15520             "dblclick" : true,
15521         /**
15522          * @event contextmenu
15523          * Fires when a template node is right clicked.
15524          * @param {Roo.View} this
15525          * @param {Number} index The index of the target node
15526          * @param {HTMLElement} node The target node
15527          * @param {Roo.EventObject} e The raw event object
15528          */
15529             "contextmenu" : true,
15530         /**
15531          * @event selectionchange
15532          * Fires when the selected nodes change.
15533          * @param {Roo.View} this
15534          * @param {Array} selections Array of the selected nodes
15535          */
15536             "selectionchange" : true,
15537     
15538         /**
15539          * @event beforeselect
15540          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15541          * @param {Roo.View} this
15542          * @param {HTMLElement} node The node to be selected
15543          * @param {Array} selections Array of currently selected nodes
15544          */
15545             "beforeselect" : true,
15546         /**
15547          * @event preparedata
15548          * Fires on every row to render, to allow you to change the data.
15549          * @param {Roo.View} this
15550          * @param {Object} data to be rendered (change this)
15551          */
15552           "preparedata" : true
15553           
15554           
15555         });
15556
15557
15558
15559     this.el.on({
15560         "click": this.onClick,
15561         "dblclick": this.onDblClick,
15562         "contextmenu": this.onContextMenu,
15563         scope:this
15564     });
15565
15566     this.selections = [];
15567     this.nodes = [];
15568     this.cmp = new Roo.CompositeElementLite([]);
15569     if(this.store){
15570         this.store = Roo.factory(this.store, Roo.data);
15571         this.setStore(this.store, true);
15572     }
15573     
15574     if ( this.footer && this.footer.xtype) {
15575            
15576          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15577         
15578         this.footer.dataSource = this.store;
15579         this.footer.container = fctr;
15580         this.footer = Roo.factory(this.footer, Roo);
15581         fctr.insertFirst(this.el);
15582         
15583         // this is a bit insane - as the paging toolbar seems to detach the el..
15584 //        dom.parentNode.parentNode.parentNode
15585          // they get detached?
15586     }
15587     
15588     
15589     Roo.View.superclass.constructor.call(this);
15590     
15591     
15592 };
15593
15594 Roo.extend(Roo.View, Roo.util.Observable, {
15595     
15596      /**
15597      * @cfg {Roo.data.Store} store Data store to load data from.
15598      */
15599     store : false,
15600     
15601     /**
15602      * @cfg {String|Roo.Element} el The container element.
15603      */
15604     el : '',
15605     
15606     /**
15607      * @cfg {String|Roo.Template} tpl The template used by this View 
15608      */
15609     tpl : false,
15610     /**
15611      * @cfg {String} dataName the named area of the template to use as the data area
15612      *                          Works with domtemplates roo-name="name"
15613      */
15614     dataName: false,
15615     /**
15616      * @cfg {String} selectedClass The css class to add to selected nodes
15617      */
15618     selectedClass : "x-view-selected",
15619      /**
15620      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15621      */
15622     emptyText : "",
15623     
15624     /**
15625      * @cfg {String} text to display on mask (default Loading)
15626      */
15627     mask : false,
15628     /**
15629      * @cfg {Boolean} multiSelect Allow multiple selection
15630      */
15631     multiSelect : false,
15632     /**
15633      * @cfg {Boolean} singleSelect Allow single selection
15634      */
15635     singleSelect:  false,
15636     
15637     /**
15638      * @cfg {Boolean} toggleSelect - selecting 
15639      */
15640     toggleSelect : false,
15641     
15642     /**
15643      * @cfg {Boolean} tickable - selecting 
15644      */
15645     tickable : false,
15646     
15647     /**
15648      * Returns the element this view is bound to.
15649      * @return {Roo.Element}
15650      */
15651     getEl : function(){
15652         return this.wrapEl;
15653     },
15654     
15655     
15656
15657     /**
15658      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15659      */
15660     refresh : function(){
15661         //Roo.log('refresh');
15662         var t = this.tpl;
15663         
15664         // if we are using something like 'domtemplate', then
15665         // the what gets used is:
15666         // t.applySubtemplate(NAME, data, wrapping data..)
15667         // the outer template then get' applied with
15668         //     the store 'extra data'
15669         // and the body get's added to the
15670         //      roo-name="data" node?
15671         //      <span class='roo-tpl-{name}'></span> ?????
15672         
15673         
15674         
15675         this.clearSelections();
15676         this.el.update("");
15677         var html = [];
15678         var records = this.store.getRange();
15679         if(records.length < 1) {
15680             
15681             // is this valid??  = should it render a template??
15682             
15683             this.el.update(this.emptyText);
15684             return;
15685         }
15686         var el = this.el;
15687         if (this.dataName) {
15688             this.el.update(t.apply(this.store.meta)); //????
15689             el = this.el.child('.roo-tpl-' + this.dataName);
15690         }
15691         
15692         for(var i = 0, len = records.length; i < len; i++){
15693             var data = this.prepareData(records[i].data, i, records[i]);
15694             this.fireEvent("preparedata", this, data, i, records[i]);
15695             
15696             var d = Roo.apply({}, data);
15697             
15698             if(this.tickable){
15699                 Roo.apply(d, {'roo-id' : Roo.id()});
15700                 
15701                 var _this = this;
15702             
15703                 Roo.each(this.parent.item, function(item){
15704                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15705                         return;
15706                     }
15707                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15708                 });
15709             }
15710             
15711             html[html.length] = Roo.util.Format.trim(
15712                 this.dataName ?
15713                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15714                     t.apply(d)
15715             );
15716         }
15717         
15718         
15719         
15720         el.update(html.join(""));
15721         this.nodes = el.dom.childNodes;
15722         this.updateIndexes(0);
15723     },
15724     
15725
15726     /**
15727      * Function to override to reformat the data that is sent to
15728      * the template for each node.
15729      * DEPRICATED - use the preparedata event handler.
15730      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15731      * a JSON object for an UpdateManager bound view).
15732      */
15733     prepareData : function(data, index, record)
15734     {
15735         this.fireEvent("preparedata", this, data, index, record);
15736         return data;
15737     },
15738
15739     onUpdate : function(ds, record){
15740         // Roo.log('on update');   
15741         this.clearSelections();
15742         var index = this.store.indexOf(record);
15743         var n = this.nodes[index];
15744         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15745         n.parentNode.removeChild(n);
15746         this.updateIndexes(index, index);
15747     },
15748
15749     
15750     
15751 // --------- FIXME     
15752     onAdd : function(ds, records, index)
15753     {
15754         //Roo.log(['on Add', ds, records, index] );        
15755         this.clearSelections();
15756         if(this.nodes.length == 0){
15757             this.refresh();
15758             return;
15759         }
15760         var n = this.nodes[index];
15761         for(var i = 0, len = records.length; i < len; i++){
15762             var d = this.prepareData(records[i].data, i, records[i]);
15763             if(n){
15764                 this.tpl.insertBefore(n, d);
15765             }else{
15766                 
15767                 this.tpl.append(this.el, d);
15768             }
15769         }
15770         this.updateIndexes(index);
15771     },
15772
15773     onRemove : function(ds, record, index){
15774        // Roo.log('onRemove');
15775         this.clearSelections();
15776         var el = this.dataName  ?
15777             this.el.child('.roo-tpl-' + this.dataName) :
15778             this.el; 
15779         
15780         el.dom.removeChild(this.nodes[index]);
15781         this.updateIndexes(index);
15782     },
15783
15784     /**
15785      * Refresh an individual node.
15786      * @param {Number} index
15787      */
15788     refreshNode : function(index){
15789         this.onUpdate(this.store, this.store.getAt(index));
15790     },
15791
15792     updateIndexes : function(startIndex, endIndex){
15793         var ns = this.nodes;
15794         startIndex = startIndex || 0;
15795         endIndex = endIndex || ns.length - 1;
15796         for(var i = startIndex; i <= endIndex; i++){
15797             ns[i].nodeIndex = i;
15798         }
15799     },
15800
15801     /**
15802      * Changes the data store this view uses and refresh the view.
15803      * @param {Store} store
15804      */
15805     setStore : function(store, initial){
15806         if(!initial && this.store){
15807             this.store.un("datachanged", this.refresh);
15808             this.store.un("add", this.onAdd);
15809             this.store.un("remove", this.onRemove);
15810             this.store.un("update", this.onUpdate);
15811             this.store.un("clear", this.refresh);
15812             this.store.un("beforeload", this.onBeforeLoad);
15813             this.store.un("load", this.onLoad);
15814             this.store.un("loadexception", this.onLoad);
15815         }
15816         if(store){
15817           
15818             store.on("datachanged", this.refresh, this);
15819             store.on("add", this.onAdd, this);
15820             store.on("remove", this.onRemove, this);
15821             store.on("update", this.onUpdate, this);
15822             store.on("clear", this.refresh, this);
15823             store.on("beforeload", this.onBeforeLoad, this);
15824             store.on("load", this.onLoad, this);
15825             store.on("loadexception", this.onLoad, this);
15826         }
15827         
15828         if(store){
15829             this.refresh();
15830         }
15831     },
15832     /**
15833      * onbeforeLoad - masks the loading area.
15834      *
15835      */
15836     onBeforeLoad : function(store,opts)
15837     {
15838          //Roo.log('onBeforeLoad');   
15839         if (!opts.add) {
15840             this.el.update("");
15841         }
15842         this.el.mask(this.mask ? this.mask : "Loading" ); 
15843     },
15844     onLoad : function ()
15845     {
15846         this.el.unmask();
15847     },
15848     
15849
15850     /**
15851      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15852      * @param {HTMLElement} node
15853      * @return {HTMLElement} The template node
15854      */
15855     findItemFromChild : function(node){
15856         var el = this.dataName  ?
15857             this.el.child('.roo-tpl-' + this.dataName,true) :
15858             this.el.dom; 
15859         
15860         if(!node || node.parentNode == el){
15861                     return node;
15862             }
15863             var p = node.parentNode;
15864             while(p && p != el){
15865             if(p.parentNode == el){
15866                 return p;
15867             }
15868             p = p.parentNode;
15869         }
15870             return null;
15871     },
15872
15873     /** @ignore */
15874     onClick : function(e){
15875         var item = this.findItemFromChild(e.getTarget());
15876         if(item){
15877             var index = this.indexOf(item);
15878             if(this.onItemClick(item, index, e) !== false){
15879                 this.fireEvent("click", this, index, item, e);
15880             }
15881         }else{
15882             this.clearSelections();
15883         }
15884     },
15885
15886     /** @ignore */
15887     onContextMenu : function(e){
15888         var item = this.findItemFromChild(e.getTarget());
15889         if(item){
15890             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15891         }
15892     },
15893
15894     /** @ignore */
15895     onDblClick : function(e){
15896         var item = this.findItemFromChild(e.getTarget());
15897         if(item){
15898             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15899         }
15900     },
15901
15902     onItemClick : function(item, index, e)
15903     {
15904         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15905             return false;
15906         }
15907         if (this.toggleSelect) {
15908             var m = this.isSelected(item) ? 'unselect' : 'select';
15909             //Roo.log(m);
15910             var _t = this;
15911             _t[m](item, true, false);
15912             return true;
15913         }
15914         if(this.multiSelect || this.singleSelect){
15915             if(this.multiSelect && e.shiftKey && this.lastSelection){
15916                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15917             }else{
15918                 this.select(item, this.multiSelect && e.ctrlKey);
15919                 this.lastSelection = item;
15920             }
15921             
15922             if(!this.tickable){
15923                 e.preventDefault();
15924             }
15925             
15926         }
15927         return true;
15928     },
15929
15930     /**
15931      * Get the number of selected nodes.
15932      * @return {Number}
15933      */
15934     getSelectionCount : function(){
15935         return this.selections.length;
15936     },
15937
15938     /**
15939      * Get the currently selected nodes.
15940      * @return {Array} An array of HTMLElements
15941      */
15942     getSelectedNodes : function(){
15943         return this.selections;
15944     },
15945
15946     /**
15947      * Get the indexes of the selected nodes.
15948      * @return {Array}
15949      */
15950     getSelectedIndexes : function(){
15951         var indexes = [], s = this.selections;
15952         for(var i = 0, len = s.length; i < len; i++){
15953             indexes.push(s[i].nodeIndex);
15954         }
15955         return indexes;
15956     },
15957
15958     /**
15959      * Clear all selections
15960      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15961      */
15962     clearSelections : function(suppressEvent){
15963         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15964             this.cmp.elements = this.selections;
15965             this.cmp.removeClass(this.selectedClass);
15966             this.selections = [];
15967             if(!suppressEvent){
15968                 this.fireEvent("selectionchange", this, this.selections);
15969             }
15970         }
15971     },
15972
15973     /**
15974      * Returns true if the passed node is selected
15975      * @param {HTMLElement/Number} node The node or node index
15976      * @return {Boolean}
15977      */
15978     isSelected : function(node){
15979         var s = this.selections;
15980         if(s.length < 1){
15981             return false;
15982         }
15983         node = this.getNode(node);
15984         return s.indexOf(node) !== -1;
15985     },
15986
15987     /**
15988      * Selects nodes.
15989      * @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
15990      * @param {Boolean} keepExisting (optional) true to keep existing selections
15991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15992      */
15993     select : function(nodeInfo, keepExisting, suppressEvent){
15994         if(nodeInfo instanceof Array){
15995             if(!keepExisting){
15996                 this.clearSelections(true);
15997             }
15998             for(var i = 0, len = nodeInfo.length; i < len; i++){
15999                 this.select(nodeInfo[i], true, true);
16000             }
16001             return;
16002         } 
16003         var node = this.getNode(nodeInfo);
16004         if(!node || this.isSelected(node)){
16005             return; // already selected.
16006         }
16007         if(!keepExisting){
16008             this.clearSelections(true);
16009         }
16010         
16011         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16012             Roo.fly(node).addClass(this.selectedClass);
16013             this.selections.push(node);
16014             if(!suppressEvent){
16015                 this.fireEvent("selectionchange", this, this.selections);
16016             }
16017         }
16018         
16019         
16020     },
16021       /**
16022      * Unselects nodes.
16023      * @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
16024      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16025      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16026      */
16027     unselect : function(nodeInfo, keepExisting, suppressEvent)
16028     {
16029         if(nodeInfo instanceof Array){
16030             Roo.each(this.selections, function(s) {
16031                 this.unselect(s, nodeInfo);
16032             }, this);
16033             return;
16034         }
16035         var node = this.getNode(nodeInfo);
16036         if(!node || !this.isSelected(node)){
16037             //Roo.log("not selected");
16038             return; // not selected.
16039         }
16040         // fireevent???
16041         var ns = [];
16042         Roo.each(this.selections, function(s) {
16043             if (s == node ) {
16044                 Roo.fly(node).removeClass(this.selectedClass);
16045
16046                 return;
16047             }
16048             ns.push(s);
16049         },this);
16050         
16051         this.selections= ns;
16052         this.fireEvent("selectionchange", this, this.selections);
16053     },
16054
16055     /**
16056      * Gets a template node.
16057      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16058      * @return {HTMLElement} The node or null if it wasn't found
16059      */
16060     getNode : function(nodeInfo){
16061         if(typeof nodeInfo == "string"){
16062             return document.getElementById(nodeInfo);
16063         }else if(typeof nodeInfo == "number"){
16064             return this.nodes[nodeInfo];
16065         }
16066         return nodeInfo;
16067     },
16068
16069     /**
16070      * Gets a range template nodes.
16071      * @param {Number} startIndex
16072      * @param {Number} endIndex
16073      * @return {Array} An array of nodes
16074      */
16075     getNodes : function(start, end){
16076         var ns = this.nodes;
16077         start = start || 0;
16078         end = typeof end == "undefined" ? ns.length - 1 : end;
16079         var nodes = [];
16080         if(start <= end){
16081             for(var i = start; i <= end; i++){
16082                 nodes.push(ns[i]);
16083             }
16084         } else{
16085             for(var i = start; i >= end; i--){
16086                 nodes.push(ns[i]);
16087             }
16088         }
16089         return nodes;
16090     },
16091
16092     /**
16093      * Finds the index of the passed node
16094      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16095      * @return {Number} The index of the node or -1
16096      */
16097     indexOf : function(node){
16098         node = this.getNode(node);
16099         if(typeof node.nodeIndex == "number"){
16100             return node.nodeIndex;
16101         }
16102         var ns = this.nodes;
16103         for(var i = 0, len = ns.length; i < len; i++){
16104             if(ns[i] == node){
16105                 return i;
16106             }
16107         }
16108         return -1;
16109     }
16110 });
16111 /*
16112  * - LGPL
16113  *
16114  * based on jquery fullcalendar
16115  * 
16116  */
16117
16118 Roo.bootstrap = Roo.bootstrap || {};
16119 /**
16120  * @class Roo.bootstrap.Calendar
16121  * @extends Roo.bootstrap.Component
16122  * Bootstrap Calendar class
16123  * @cfg {Boolean} loadMask (true|false) default false
16124  * @cfg {Object} header generate the user specific header of the calendar, default false
16125
16126  * @constructor
16127  * Create a new Container
16128  * @param {Object} config The config object
16129  */
16130
16131
16132
16133 Roo.bootstrap.Calendar = function(config){
16134     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16135      this.addEvents({
16136         /**
16137              * @event select
16138              * Fires when a date is selected
16139              * @param {DatePicker} this
16140              * @param {Date} date The selected date
16141              */
16142         'select': true,
16143         /**
16144              * @event monthchange
16145              * Fires when the displayed month changes 
16146              * @param {DatePicker} this
16147              * @param {Date} date The selected month
16148              */
16149         'monthchange': true,
16150         /**
16151              * @event evententer
16152              * Fires when mouse over an event
16153              * @param {Calendar} this
16154              * @param {event} Event
16155              */
16156         'evententer': true,
16157         /**
16158              * @event eventleave
16159              * Fires when the mouse leaves an
16160              * @param {Calendar} this
16161              * @param {event}
16162              */
16163         'eventleave': true,
16164         /**
16165              * @event eventclick
16166              * Fires when the mouse click an
16167              * @param {Calendar} this
16168              * @param {event}
16169              */
16170         'eventclick': true
16171         
16172     });
16173
16174 };
16175
16176 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16177     
16178      /**
16179      * @cfg {Number} startDay
16180      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16181      */
16182     startDay : 0,
16183     
16184     loadMask : false,
16185     
16186     header : false,
16187       
16188     getAutoCreate : function(){
16189         
16190         
16191         var fc_button = function(name, corner, style, content ) {
16192             return Roo.apply({},{
16193                 tag : 'span',
16194                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16195                          (corner.length ?
16196                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16197                             ''
16198                         ),
16199                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16200                 unselectable: 'on'
16201             });
16202         };
16203         
16204         var header = {};
16205         
16206         if(!this.header){
16207             header = {
16208                 tag : 'table',
16209                 cls : 'fc-header',
16210                 style : 'width:100%',
16211                 cn : [
16212                     {
16213                         tag: 'tr',
16214                         cn : [
16215                             {
16216                                 tag : 'td',
16217                                 cls : 'fc-header-left',
16218                                 cn : [
16219                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16220                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16221                                     { tag: 'span', cls: 'fc-header-space' },
16222                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16223
16224
16225                                 ]
16226                             },
16227
16228                             {
16229                                 tag : 'td',
16230                                 cls : 'fc-header-center',
16231                                 cn : [
16232                                     {
16233                                         tag: 'span',
16234                                         cls: 'fc-header-title',
16235                                         cn : {
16236                                             tag: 'H2',
16237                                             html : 'month / year'
16238                                         }
16239                                     }
16240
16241                                 ]
16242                             },
16243                             {
16244                                 tag : 'td',
16245                                 cls : 'fc-header-right',
16246                                 cn : [
16247                               /*      fc_button('month', 'left', '', 'month' ),
16248                                     fc_button('week', '', '', 'week' ),
16249                                     fc_button('day', 'right', '', 'day' )
16250                                 */    
16251
16252                                 ]
16253                             }
16254
16255                         ]
16256                     }
16257                 ]
16258             };
16259         }
16260         
16261         header = this.header;
16262         
16263        
16264         var cal_heads = function() {
16265             var ret = [];
16266             // fixme - handle this.
16267             
16268             for (var i =0; i < Date.dayNames.length; i++) {
16269                 var d = Date.dayNames[i];
16270                 ret.push({
16271                     tag: 'th',
16272                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16273                     html : d.substring(0,3)
16274                 });
16275                 
16276             }
16277             ret[0].cls += ' fc-first';
16278             ret[6].cls += ' fc-last';
16279             return ret;
16280         };
16281         var cal_cell = function(n) {
16282             return  {
16283                 tag: 'td',
16284                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16285                 cn : [
16286                     {
16287                         cn : [
16288                             {
16289                                 cls: 'fc-day-number',
16290                                 html: 'D'
16291                             },
16292                             {
16293                                 cls: 'fc-day-content',
16294                              
16295                                 cn : [
16296                                      {
16297                                         style: 'position: relative;' // height: 17px;
16298                                     }
16299                                 ]
16300                             }
16301                             
16302                             
16303                         ]
16304                     }
16305                 ]
16306                 
16307             }
16308         };
16309         var cal_rows = function() {
16310             
16311             var ret = [];
16312             for (var r = 0; r < 6; r++) {
16313                 var row= {
16314                     tag : 'tr',
16315                     cls : 'fc-week',
16316                     cn : []
16317                 };
16318                 
16319                 for (var i =0; i < Date.dayNames.length; i++) {
16320                     var d = Date.dayNames[i];
16321                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16322
16323                 }
16324                 row.cn[0].cls+=' fc-first';
16325                 row.cn[0].cn[0].style = 'min-height:90px';
16326                 row.cn[6].cls+=' fc-last';
16327                 ret.push(row);
16328                 
16329             }
16330             ret[0].cls += ' fc-first';
16331             ret[4].cls += ' fc-prev-last';
16332             ret[5].cls += ' fc-last';
16333             return ret;
16334             
16335         };
16336         
16337         var cal_table = {
16338             tag: 'table',
16339             cls: 'fc-border-separate',
16340             style : 'width:100%',
16341             cellspacing  : 0,
16342             cn : [
16343                 { 
16344                     tag: 'thead',
16345                     cn : [
16346                         { 
16347                             tag: 'tr',
16348                             cls : 'fc-first fc-last',
16349                             cn : cal_heads()
16350                         }
16351                     ]
16352                 },
16353                 { 
16354                     tag: 'tbody',
16355                     cn : cal_rows()
16356                 }
16357                   
16358             ]
16359         };
16360          
16361          var cfg = {
16362             cls : 'fc fc-ltr',
16363             cn : [
16364                 header,
16365                 {
16366                     cls : 'fc-content',
16367                     style : "position: relative;",
16368                     cn : [
16369                         {
16370                             cls : 'fc-view fc-view-month fc-grid',
16371                             style : 'position: relative',
16372                             unselectable : 'on',
16373                             cn : [
16374                                 {
16375                                     cls : 'fc-event-container',
16376                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16377                                 },
16378                                 cal_table
16379                             ]
16380                         }
16381                     ]
16382     
16383                 }
16384            ] 
16385             
16386         };
16387         
16388          
16389         
16390         return cfg;
16391     },
16392     
16393     
16394     initEvents : function()
16395     {
16396         if(!this.store){
16397             throw "can not find store for calendar";
16398         }
16399         
16400         var mark = {
16401             tag: "div",
16402             cls:"x-dlg-mask",
16403             style: "text-align:center",
16404             cn: [
16405                 {
16406                     tag: "div",
16407                     style: "background-color:white;width:50%;margin:250 auto",
16408                     cn: [
16409                         {
16410                             tag: "img",
16411                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16412                         },
16413                         {
16414                             tag: "span",
16415                             html: "Loading"
16416                         }
16417                         
16418                     ]
16419                 }
16420             ]
16421         };
16422         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16423         
16424         var size = this.el.select('.fc-content', true).first().getSize();
16425         this.maskEl.setSize(size.width, size.height);
16426         this.maskEl.enableDisplayMode("block");
16427         if(!this.loadMask){
16428             this.maskEl.hide();
16429         }
16430         
16431         this.store = Roo.factory(this.store, Roo.data);
16432         this.store.on('load', this.onLoad, this);
16433         this.store.on('beforeload', this.onBeforeLoad, this);
16434         
16435         this.resize();
16436         
16437         this.cells = this.el.select('.fc-day',true);
16438         //Roo.log(this.cells);
16439         this.textNodes = this.el.query('.fc-day-number');
16440         this.cells.addClassOnOver('fc-state-hover');
16441         
16442         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16443         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16444         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16445         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16446         
16447         this.on('monthchange', this.onMonthChange, this);
16448         
16449         this.update(new Date().clearTime());
16450     },
16451     
16452     resize : function() {
16453         var sz  = this.el.getSize();
16454         
16455         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16456         this.el.select('.fc-day-content div',true).setHeight(34);
16457     },
16458     
16459     
16460     // private
16461     showPrevMonth : function(e){
16462         this.update(this.activeDate.add("mo", -1));
16463     },
16464     showToday : function(e){
16465         this.update(new Date().clearTime());
16466     },
16467     // private
16468     showNextMonth : function(e){
16469         this.update(this.activeDate.add("mo", 1));
16470     },
16471
16472     // private
16473     showPrevYear : function(){
16474         this.update(this.activeDate.add("y", -1));
16475     },
16476
16477     // private
16478     showNextYear : function(){
16479         this.update(this.activeDate.add("y", 1));
16480     },
16481
16482     
16483    // private
16484     update : function(date)
16485     {
16486         var vd = this.activeDate;
16487         this.activeDate = date;
16488 //        if(vd && this.el){
16489 //            var t = date.getTime();
16490 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16491 //                Roo.log('using add remove');
16492 //                
16493 //                this.fireEvent('monthchange', this, date);
16494 //                
16495 //                this.cells.removeClass("fc-state-highlight");
16496 //                this.cells.each(function(c){
16497 //                   if(c.dateValue == t){
16498 //                       c.addClass("fc-state-highlight");
16499 //                       setTimeout(function(){
16500 //                            try{c.dom.firstChild.focus();}catch(e){}
16501 //                       }, 50);
16502 //                       return false;
16503 //                   }
16504 //                   return true;
16505 //                });
16506 //                return;
16507 //            }
16508 //        }
16509         
16510         var days = date.getDaysInMonth();
16511         
16512         var firstOfMonth = date.getFirstDateOfMonth();
16513         var startingPos = firstOfMonth.getDay()-this.startDay;
16514         
16515         if(startingPos < this.startDay){
16516             startingPos += 7;
16517         }
16518         
16519         var pm = date.add(Date.MONTH, -1);
16520         var prevStart = pm.getDaysInMonth()-startingPos;
16521 //        
16522         this.cells = this.el.select('.fc-day',true);
16523         this.textNodes = this.el.query('.fc-day-number');
16524         this.cells.addClassOnOver('fc-state-hover');
16525         
16526         var cells = this.cells.elements;
16527         var textEls = this.textNodes;
16528         
16529         Roo.each(cells, function(cell){
16530             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16531         });
16532         
16533         days += startingPos;
16534
16535         // convert everything to numbers so it's fast
16536         var day = 86400000;
16537         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16538         //Roo.log(d);
16539         //Roo.log(pm);
16540         //Roo.log(prevStart);
16541         
16542         var today = new Date().clearTime().getTime();
16543         var sel = date.clearTime().getTime();
16544         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16545         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16546         var ddMatch = this.disabledDatesRE;
16547         var ddText = this.disabledDatesText;
16548         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16549         var ddaysText = this.disabledDaysText;
16550         var format = this.format;
16551         
16552         var setCellClass = function(cal, cell){
16553             cell.row = 0;
16554             cell.events = [];
16555             cell.more = [];
16556             //Roo.log('set Cell Class');
16557             cell.title = "";
16558             var t = d.getTime();
16559             
16560             //Roo.log(d);
16561             
16562             cell.dateValue = t;
16563             if(t == today){
16564                 cell.className += " fc-today";
16565                 cell.className += " fc-state-highlight";
16566                 cell.title = cal.todayText;
16567             }
16568             if(t == sel){
16569                 // disable highlight in other month..
16570                 //cell.className += " fc-state-highlight";
16571                 
16572             }
16573             // disabling
16574             if(t < min) {
16575                 cell.className = " fc-state-disabled";
16576                 cell.title = cal.minText;
16577                 return;
16578             }
16579             if(t > max) {
16580                 cell.className = " fc-state-disabled";
16581                 cell.title = cal.maxText;
16582                 return;
16583             }
16584             if(ddays){
16585                 if(ddays.indexOf(d.getDay()) != -1){
16586                     cell.title = ddaysText;
16587                     cell.className = " fc-state-disabled";
16588                 }
16589             }
16590             if(ddMatch && format){
16591                 var fvalue = d.dateFormat(format);
16592                 if(ddMatch.test(fvalue)){
16593                     cell.title = ddText.replace("%0", fvalue);
16594                     cell.className = " fc-state-disabled";
16595                 }
16596             }
16597             
16598             if (!cell.initialClassName) {
16599                 cell.initialClassName = cell.dom.className;
16600             }
16601             
16602             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16603         };
16604
16605         var i = 0;
16606         
16607         for(; i < startingPos; i++) {
16608             textEls[i].innerHTML = (++prevStart);
16609             d.setDate(d.getDate()+1);
16610             
16611             cells[i].className = "fc-past fc-other-month";
16612             setCellClass(this, cells[i]);
16613         }
16614         
16615         var intDay = 0;
16616         
16617         for(; i < days; i++){
16618             intDay = i - startingPos + 1;
16619             textEls[i].innerHTML = (intDay);
16620             d.setDate(d.getDate()+1);
16621             
16622             cells[i].className = ''; // "x-date-active";
16623             setCellClass(this, cells[i]);
16624         }
16625         var extraDays = 0;
16626         
16627         for(; i < 42; i++) {
16628             textEls[i].innerHTML = (++extraDays);
16629             d.setDate(d.getDate()+1);
16630             
16631             cells[i].className = "fc-future fc-other-month";
16632             setCellClass(this, cells[i]);
16633         }
16634         
16635         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16636         
16637         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16638         
16639         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16640         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16641         
16642         if(totalRows != 6){
16643             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16644             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16645         }
16646         
16647         this.fireEvent('monthchange', this, date);
16648         
16649         
16650         /*
16651         if(!this.internalRender){
16652             var main = this.el.dom.firstChild;
16653             var w = main.offsetWidth;
16654             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16655             Roo.fly(main).setWidth(w);
16656             this.internalRender = true;
16657             // opera does not respect the auto grow header center column
16658             // then, after it gets a width opera refuses to recalculate
16659             // without a second pass
16660             if(Roo.isOpera && !this.secondPass){
16661                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16662                 this.secondPass = true;
16663                 this.update.defer(10, this, [date]);
16664             }
16665         }
16666         */
16667         
16668     },
16669     
16670     findCell : function(dt) {
16671         dt = dt.clearTime().getTime();
16672         var ret = false;
16673         this.cells.each(function(c){
16674             //Roo.log("check " +c.dateValue + '?=' + dt);
16675             if(c.dateValue == dt){
16676                 ret = c;
16677                 return false;
16678             }
16679             return true;
16680         });
16681         
16682         return ret;
16683     },
16684     
16685     findCells : function(ev) {
16686         var s = ev.start.clone().clearTime().getTime();
16687        // Roo.log(s);
16688         var e= ev.end.clone().clearTime().getTime();
16689        // Roo.log(e);
16690         var ret = [];
16691         this.cells.each(function(c){
16692              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16693             
16694             if(c.dateValue > e){
16695                 return ;
16696             }
16697             if(c.dateValue < s){
16698                 return ;
16699             }
16700             ret.push(c);
16701         });
16702         
16703         return ret;    
16704     },
16705     
16706 //    findBestRow: function(cells)
16707 //    {
16708 //        var ret = 0;
16709 //        
16710 //        for (var i =0 ; i < cells.length;i++) {
16711 //            ret  = Math.max(cells[i].rows || 0,ret);
16712 //        }
16713 //        return ret;
16714 //        
16715 //    },
16716     
16717     
16718     addItem : function(ev)
16719     {
16720         // look for vertical location slot in
16721         var cells = this.findCells(ev);
16722         
16723 //        ev.row = this.findBestRow(cells);
16724         
16725         // work out the location.
16726         
16727         var crow = false;
16728         var rows = [];
16729         for(var i =0; i < cells.length; i++) {
16730             
16731             cells[i].row = cells[0].row;
16732             
16733             if(i == 0){
16734                 cells[i].row = cells[i].row + 1;
16735             }
16736             
16737             if (!crow) {
16738                 crow = {
16739                     start : cells[i],
16740                     end :  cells[i]
16741                 };
16742                 continue;
16743             }
16744             if (crow.start.getY() == cells[i].getY()) {
16745                 // on same row.
16746                 crow.end = cells[i];
16747                 continue;
16748             }
16749             // different row.
16750             rows.push(crow);
16751             crow = {
16752                 start: cells[i],
16753                 end : cells[i]
16754             };
16755             
16756         }
16757         
16758         rows.push(crow);
16759         ev.els = [];
16760         ev.rows = rows;
16761         ev.cells = cells;
16762         
16763         cells[0].events.push(ev);
16764         
16765         this.calevents.push(ev);
16766     },
16767     
16768     clearEvents: function() {
16769         
16770         if(!this.calevents){
16771             return;
16772         }
16773         
16774         Roo.each(this.cells.elements, function(c){
16775             c.row = 0;
16776             c.events = [];
16777             c.more = [];
16778         });
16779         
16780         Roo.each(this.calevents, function(e) {
16781             Roo.each(e.els, function(el) {
16782                 el.un('mouseenter' ,this.onEventEnter, this);
16783                 el.un('mouseleave' ,this.onEventLeave, this);
16784                 el.remove();
16785             },this);
16786         },this);
16787         
16788         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16789             e.remove();
16790         });
16791         
16792     },
16793     
16794     renderEvents: function()
16795     {   
16796         var _this = this;
16797         
16798         this.cells.each(function(c) {
16799             
16800             if(c.row < 5){
16801                 return;
16802             }
16803             
16804             var ev = c.events;
16805             
16806             var r = 4;
16807             if(c.row != c.events.length){
16808                 r = 4 - (4 - (c.row - c.events.length));
16809             }
16810             
16811             c.events = ev.slice(0, r);
16812             c.more = ev.slice(r);
16813             
16814             if(c.more.length && c.more.length == 1){
16815                 c.events.push(c.more.pop());
16816             }
16817             
16818             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16819             
16820         });
16821             
16822         this.cells.each(function(c) {
16823             
16824             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16825             
16826             
16827             for (var e = 0; e < c.events.length; e++){
16828                 var ev = c.events[e];
16829                 var rows = ev.rows;
16830                 
16831                 for(var i = 0; i < rows.length; i++) {
16832                 
16833                     // how many rows should it span..
16834
16835                     var  cfg = {
16836                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16837                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16838
16839                         unselectable : "on",
16840                         cn : [
16841                             {
16842                                 cls: 'fc-event-inner',
16843                                 cn : [
16844     //                                {
16845     //                                  tag:'span',
16846     //                                  cls: 'fc-event-time',
16847     //                                  html : cells.length > 1 ? '' : ev.time
16848     //                                },
16849                                     {
16850                                       tag:'span',
16851                                       cls: 'fc-event-title',
16852                                       html : String.format('{0}', ev.title)
16853                                     }
16854
16855
16856                                 ]
16857                             },
16858                             {
16859                                 cls: 'ui-resizable-handle ui-resizable-e',
16860                                 html : '&nbsp;&nbsp;&nbsp'
16861                             }
16862
16863                         ]
16864                     };
16865
16866                     if (i == 0) {
16867                         cfg.cls += ' fc-event-start';
16868                     }
16869                     if ((i+1) == rows.length) {
16870                         cfg.cls += ' fc-event-end';
16871                     }
16872
16873                     var ctr = _this.el.select('.fc-event-container',true).first();
16874                     var cg = ctr.createChild(cfg);
16875
16876                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16877                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16878
16879                     var r = (c.more.length) ? 1 : 0;
16880                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16881                     cg.setWidth(ebox.right - sbox.x -2);
16882
16883                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16884                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16885                     cg.on('click', _this.onEventClick, _this, ev);
16886
16887                     ev.els.push(cg);
16888                     
16889                 }
16890                 
16891             }
16892             
16893             
16894             if(c.more.length){
16895                 var  cfg = {
16896                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16897                     style : 'position: absolute',
16898                     unselectable : "on",
16899                     cn : [
16900                         {
16901                             cls: 'fc-event-inner',
16902                             cn : [
16903                                 {
16904                                   tag:'span',
16905                                   cls: 'fc-event-title',
16906                                   html : 'More'
16907                                 }
16908
16909
16910                             ]
16911                         },
16912                         {
16913                             cls: 'ui-resizable-handle ui-resizable-e',
16914                             html : '&nbsp;&nbsp;&nbsp'
16915                         }
16916
16917                     ]
16918                 };
16919
16920                 var ctr = _this.el.select('.fc-event-container',true).first();
16921                 var cg = ctr.createChild(cfg);
16922
16923                 var sbox = c.select('.fc-day-content',true).first().getBox();
16924                 var ebox = c.select('.fc-day-content',true).first().getBox();
16925                 //Roo.log(cg);
16926                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16927                 cg.setWidth(ebox.right - sbox.x -2);
16928
16929                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16930                 
16931             }
16932             
16933         });
16934         
16935         
16936         
16937     },
16938     
16939     onEventEnter: function (e, el,event,d) {
16940         this.fireEvent('evententer', this, el, event);
16941     },
16942     
16943     onEventLeave: function (e, el,event,d) {
16944         this.fireEvent('eventleave', this, el, event);
16945     },
16946     
16947     onEventClick: function (e, el,event,d) {
16948         this.fireEvent('eventclick', this, el, event);
16949     },
16950     
16951     onMonthChange: function () {
16952         this.store.load();
16953     },
16954     
16955     onMoreEventClick: function(e, el, more)
16956     {
16957         var _this = this;
16958         
16959         this.calpopover.placement = 'right';
16960         this.calpopover.setTitle('More');
16961         
16962         this.calpopover.setContent('');
16963         
16964         var ctr = this.calpopover.el.select('.popover-content', true).first();
16965         
16966         Roo.each(more, function(m){
16967             var cfg = {
16968                 cls : 'fc-event-hori fc-event-draggable',
16969                 html : m.title
16970             };
16971             var cg = ctr.createChild(cfg);
16972             
16973             cg.on('click', _this.onEventClick, _this, m);
16974         });
16975         
16976         this.calpopover.show(el);
16977         
16978         
16979     },
16980     
16981     onLoad: function () 
16982     {   
16983         this.calevents = [];
16984         var cal = this;
16985         
16986         if(this.store.getCount() > 0){
16987             this.store.data.each(function(d){
16988                cal.addItem({
16989                     id : d.data.id,
16990                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16991                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16992                     time : d.data.start_time,
16993                     title : d.data.title,
16994                     description : d.data.description,
16995                     venue : d.data.venue
16996                 });
16997             });
16998         }
16999         
17000         this.renderEvents();
17001         
17002         if(this.calevents.length && this.loadMask){
17003             this.maskEl.hide();
17004         }
17005     },
17006     
17007     onBeforeLoad: function()
17008     {
17009         this.clearEvents();
17010         if(this.loadMask){
17011             this.maskEl.show();
17012         }
17013     }
17014 });
17015
17016  
17017  /*
17018  * - LGPL
17019  *
17020  * element
17021  * 
17022  */
17023
17024 /**
17025  * @class Roo.bootstrap.Popover
17026  * @extends Roo.bootstrap.Component
17027  * Bootstrap Popover class
17028  * @cfg {String} html contents of the popover   (or false to use children..)
17029  * @cfg {String} title of popover (or false to hide)
17030  * @cfg {String} placement how it is placed
17031  * @cfg {String} trigger click || hover (or false to trigger manually)
17032  * @cfg {String} over what (parent or false to trigger manually.)
17033  * @cfg {Number} delay - delay before showing
17034  
17035  * @constructor
17036  * Create a new Popover
17037  * @param {Object} config The config object
17038  */
17039
17040 Roo.bootstrap.Popover = function(config){
17041     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17042     
17043     this.addEvents({
17044         // raw events
17045          /**
17046          * @event show
17047          * After the popover show
17048          * 
17049          * @param {Roo.bootstrap.Popover} this
17050          */
17051         "show" : true,
17052         /**
17053          * @event hide
17054          * After the popover hide
17055          * 
17056          * @param {Roo.bootstrap.Popover} this
17057          */
17058         "hide" : true
17059     });
17060 };
17061
17062 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17063     
17064     title: 'Fill in a title',
17065     html: false,
17066     
17067     placement : 'right',
17068     trigger : 'hover', // hover
17069     
17070     delay : 0,
17071     
17072     over: 'parent',
17073     
17074     can_build_overlaid : false,
17075     
17076     getChildContainer : function()
17077     {
17078         return this.el.select('.popover-content',true).first();
17079     },
17080     
17081     getAutoCreate : function(){
17082          
17083         var cfg = {
17084            cls : 'popover roo-dynamic',
17085            style: 'display:block',
17086            cn : [
17087                 {
17088                     cls : 'arrow'
17089                 },
17090                 {
17091                     cls : 'popover-inner',
17092                     cn : [
17093                         {
17094                             tag: 'h3',
17095                             cls: 'popover-title',
17096                             html : this.title
17097                         },
17098                         {
17099                             cls : 'popover-content',
17100                             html : this.html
17101                         }
17102                     ]
17103                     
17104                 }
17105            ]
17106         };
17107         
17108         return cfg;
17109     },
17110     setTitle: function(str)
17111     {
17112         this.title = str;
17113         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17114     },
17115     setContent: function(str)
17116     {
17117         this.html = str;
17118         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17119     },
17120     // as it get's added to the bottom of the page.
17121     onRender : function(ct, position)
17122     {
17123         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17124         if(!this.el){
17125             var cfg = Roo.apply({},  this.getAutoCreate());
17126             cfg.id = Roo.id();
17127             
17128             if (this.cls) {
17129                 cfg.cls += ' ' + this.cls;
17130             }
17131             if (this.style) {
17132                 cfg.style = this.style;
17133             }
17134             //Roo.log("adding to ");
17135             this.el = Roo.get(document.body).createChild(cfg, position);
17136 //            Roo.log(this.el);
17137         }
17138         this.initEvents();
17139     },
17140     
17141     initEvents : function()
17142     {
17143         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17144         this.el.enableDisplayMode('block');
17145         this.el.hide();
17146         if (this.over === false) {
17147             return; 
17148         }
17149         if (this.triggers === false) {
17150             return;
17151         }
17152         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17153         var triggers = this.trigger ? this.trigger.split(' ') : [];
17154         Roo.each(triggers, function(trigger) {
17155         
17156             if (trigger == 'click') {
17157                 on_el.on('click', this.toggle, this);
17158             } else if (trigger != 'manual') {
17159                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17160                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17161       
17162                 on_el.on(eventIn  ,this.enter, this);
17163                 on_el.on(eventOut, this.leave, this);
17164             }
17165         }, this);
17166         
17167     },
17168     
17169     
17170     // private
17171     timeout : null,
17172     hoverState : null,
17173     
17174     toggle : function () {
17175         this.hoverState == 'in' ? this.leave() : this.enter();
17176     },
17177     
17178     enter : function () {
17179         
17180         clearTimeout(this.timeout);
17181     
17182         this.hoverState = 'in';
17183     
17184         if (!this.delay || !this.delay.show) {
17185             this.show();
17186             return;
17187         }
17188         var _t = this;
17189         this.timeout = setTimeout(function () {
17190             if (_t.hoverState == 'in') {
17191                 _t.show();
17192             }
17193         }, this.delay.show)
17194     },
17195     
17196     leave : function() {
17197         clearTimeout(this.timeout);
17198     
17199         this.hoverState = 'out';
17200     
17201         if (!this.delay || !this.delay.hide) {
17202             this.hide();
17203             return;
17204         }
17205         var _t = this;
17206         this.timeout = setTimeout(function () {
17207             if (_t.hoverState == 'out') {
17208                 _t.hide();
17209             }
17210         }, this.delay.hide)
17211     },
17212     
17213     show : function (on_el)
17214     {
17215         if (!on_el) {
17216             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17217         }
17218         
17219         // set content.
17220         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17221         if (this.html !== false) {
17222             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17223         }
17224         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17225         if (!this.title.length) {
17226             this.el.select('.popover-title',true).hide();
17227         }
17228         
17229         var placement = typeof this.placement == 'function' ?
17230             this.placement.call(this, this.el, on_el) :
17231             this.placement;
17232             
17233         var autoToken = /\s?auto?\s?/i;
17234         var autoPlace = autoToken.test(placement);
17235         if (autoPlace) {
17236             placement = placement.replace(autoToken, '') || 'top';
17237         }
17238         
17239         //this.el.detach()
17240         //this.el.setXY([0,0]);
17241         this.el.show();
17242         this.el.dom.style.display='block';
17243         this.el.addClass(placement);
17244         
17245         //this.el.appendTo(on_el);
17246         
17247         var p = this.getPosition();
17248         var box = this.el.getBox();
17249         
17250         if (autoPlace) {
17251             // fixme..
17252         }
17253         var align = Roo.bootstrap.Popover.alignment[placement];
17254         this.el.alignTo(on_el, align[0],align[1]);
17255         //var arrow = this.el.select('.arrow',true).first();
17256         //arrow.set(align[2], 
17257         
17258         this.el.addClass('in');
17259         
17260         
17261         if (this.el.hasClass('fade')) {
17262             // fade it?
17263         }
17264         
17265         this.hoverState = 'in';
17266         
17267         this.fireEvent('show', this);
17268         
17269     },
17270     hide : function()
17271     {
17272         this.el.setXY([0,0]);
17273         this.el.removeClass('in');
17274         this.el.hide();
17275         this.hoverState = null;
17276         
17277         this.fireEvent('hide', this);
17278     }
17279     
17280 });
17281
17282 Roo.bootstrap.Popover.alignment = {
17283     'left' : ['r-l', [-10,0], 'right'],
17284     'right' : ['l-r', [10,0], 'left'],
17285     'bottom' : ['t-b', [0,10], 'top'],
17286     'top' : [ 'b-t', [0,-10], 'bottom']
17287 };
17288
17289  /*
17290  * - LGPL
17291  *
17292  * Progress
17293  * 
17294  */
17295
17296 /**
17297  * @class Roo.bootstrap.Progress
17298  * @extends Roo.bootstrap.Component
17299  * Bootstrap Progress class
17300  * @cfg {Boolean} striped striped of the progress bar
17301  * @cfg {Boolean} active animated of the progress bar
17302  * 
17303  * 
17304  * @constructor
17305  * Create a new Progress
17306  * @param {Object} config The config object
17307  */
17308
17309 Roo.bootstrap.Progress = function(config){
17310     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17311 };
17312
17313 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17314     
17315     striped : false,
17316     active: false,
17317     
17318     getAutoCreate : function(){
17319         var cfg = {
17320             tag: 'div',
17321             cls: 'progress'
17322         };
17323         
17324         
17325         if(this.striped){
17326             cfg.cls += ' progress-striped';
17327         }
17328       
17329         if(this.active){
17330             cfg.cls += ' active';
17331         }
17332         
17333         
17334         return cfg;
17335     }
17336    
17337 });
17338
17339  
17340
17341  /*
17342  * - LGPL
17343  *
17344  * ProgressBar
17345  * 
17346  */
17347
17348 /**
17349  * @class Roo.bootstrap.ProgressBar
17350  * @extends Roo.bootstrap.Component
17351  * Bootstrap ProgressBar class
17352  * @cfg {Number} aria_valuenow aria-value now
17353  * @cfg {Number} aria_valuemin aria-value min
17354  * @cfg {Number} aria_valuemax aria-value max
17355  * @cfg {String} label label for the progress bar
17356  * @cfg {String} panel (success | info | warning | danger )
17357  * @cfg {String} role role of the progress bar
17358  * @cfg {String} sr_only text
17359  * 
17360  * 
17361  * @constructor
17362  * Create a new ProgressBar
17363  * @param {Object} config The config object
17364  */
17365
17366 Roo.bootstrap.ProgressBar = function(config){
17367     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17368 };
17369
17370 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17371     
17372     aria_valuenow : 0,
17373     aria_valuemin : 0,
17374     aria_valuemax : 100,
17375     label : false,
17376     panel : false,
17377     role : false,
17378     sr_only: false,
17379     
17380     getAutoCreate : function()
17381     {
17382         
17383         var cfg = {
17384             tag: 'div',
17385             cls: 'progress-bar',
17386             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17387         };
17388         
17389         if(this.sr_only){
17390             cfg.cn = {
17391                 tag: 'span',
17392                 cls: 'sr-only',
17393                 html: this.sr_only
17394             }
17395         }
17396         
17397         if(this.role){
17398             cfg.role = this.role;
17399         }
17400         
17401         if(this.aria_valuenow){
17402             cfg['aria-valuenow'] = this.aria_valuenow;
17403         }
17404         
17405         if(this.aria_valuemin){
17406             cfg['aria-valuemin'] = this.aria_valuemin;
17407         }
17408         
17409         if(this.aria_valuemax){
17410             cfg['aria-valuemax'] = this.aria_valuemax;
17411         }
17412         
17413         if(this.label && !this.sr_only){
17414             cfg.html = this.label;
17415         }
17416         
17417         if(this.panel){
17418             cfg.cls += ' progress-bar-' + this.panel;
17419         }
17420         
17421         return cfg;
17422     },
17423     
17424     update : function(aria_valuenow)
17425     {
17426         this.aria_valuenow = aria_valuenow;
17427         
17428         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17429     }
17430    
17431 });
17432
17433  
17434
17435  /*
17436  * - LGPL
17437  *
17438  * column
17439  * 
17440  */
17441
17442 /**
17443  * @class Roo.bootstrap.TabGroup
17444  * @extends Roo.bootstrap.Column
17445  * Bootstrap Column class
17446  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17447  * @cfg {Boolean} carousel true to make the group behave like a carousel
17448  * @cfg {Boolean} bullets show bullets for the panels
17449  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17450  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17451  * @cfg {Boolean} showarrow (true|false) show arrow default true
17452  * 
17453  * @constructor
17454  * Create a new TabGroup
17455  * @param {Object} config The config object
17456  */
17457
17458 Roo.bootstrap.TabGroup = function(config){
17459     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17460     if (!this.navId) {
17461         this.navId = Roo.id();
17462     }
17463     this.tabs = [];
17464     Roo.bootstrap.TabGroup.register(this);
17465     
17466 };
17467
17468 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17469     
17470     carousel : false,
17471     transition : false,
17472     bullets : 0,
17473     timer : 0,
17474     autoslide : false,
17475     slideFn : false,
17476     slideOnTouch : false,
17477     showarrow : true,
17478     
17479     getAutoCreate : function()
17480     {
17481         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17482         
17483         cfg.cls += ' tab-content';
17484         
17485         if (this.carousel) {
17486             cfg.cls += ' carousel slide';
17487             
17488             cfg.cn = [{
17489                cls : 'carousel-inner',
17490                cn : []
17491             }];
17492         
17493             if(this.bullets  && !Roo.isTouch){
17494                 
17495                 var bullets = {
17496                     cls : 'carousel-bullets',
17497                     cn : []
17498                 };
17499                
17500                 if(this.bullets_cls){
17501                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17502                 }
17503                 
17504                 bullets.cn.push({
17505                     cls : 'clear'
17506                 });
17507                 
17508                 cfg.cn[0].cn.push(bullets);
17509             }
17510             
17511             if(this.showarrow){
17512                 cfg.cn[0].cn.push({
17513                     tag : 'div',
17514                     class : 'carousel-arrow',
17515                     cn : [
17516                         {
17517                             tag : 'div',
17518                             class : 'carousel-prev',
17519                             cn : [
17520                                 {
17521                                     tag : 'i',
17522                                     class : 'fa fa-chevron-left'
17523                                 }
17524                             ]
17525                         },
17526                         {
17527                             tag : 'div',
17528                             class : 'carousel-next',
17529                             cn : [
17530                                 {
17531                                     tag : 'i',
17532                                     class : 'fa fa-chevron-right'
17533                                 }
17534                             ]
17535                         }
17536                     ]
17537                 });
17538             }
17539             
17540         }
17541         
17542         return cfg;
17543     },
17544     
17545     initEvents:  function()
17546     {
17547 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17548 //            this.el.on("touchstart", this.onTouchStart, this);
17549 //        }
17550         
17551         if(this.autoslide){
17552             var _this = this;
17553             
17554             this.slideFn = window.setInterval(function() {
17555                 _this.showPanelNext();
17556             }, this.timer);
17557         }
17558         
17559         if(this.showarrow){
17560             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17561             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17562         }
17563         
17564         
17565     },
17566     
17567 //    onTouchStart : function(e, el, o)
17568 //    {
17569 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17570 //            return;
17571 //        }
17572 //        
17573 //        this.showPanelNext();
17574 //    },
17575     
17576     
17577     getChildContainer : function()
17578     {
17579         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17580     },
17581     
17582     /**
17583     * register a Navigation item
17584     * @param {Roo.bootstrap.NavItem} the navitem to add
17585     */
17586     register : function(item)
17587     {
17588         this.tabs.push( item);
17589         item.navId = this.navId; // not really needed..
17590         this.addBullet();
17591     
17592     },
17593     
17594     getActivePanel : function()
17595     {
17596         var r = false;
17597         Roo.each(this.tabs, function(t) {
17598             if (t.active) {
17599                 r = t;
17600                 return false;
17601             }
17602             return null;
17603         });
17604         return r;
17605         
17606     },
17607     getPanelByName : function(n)
17608     {
17609         var r = false;
17610         Roo.each(this.tabs, function(t) {
17611             if (t.tabId == n) {
17612                 r = t;
17613                 return false;
17614             }
17615             return null;
17616         });
17617         return r;
17618     },
17619     indexOfPanel : function(p)
17620     {
17621         var r = false;
17622         Roo.each(this.tabs, function(t,i) {
17623             if (t.tabId == p.tabId) {
17624                 r = i;
17625                 return false;
17626             }
17627             return null;
17628         });
17629         return r;
17630     },
17631     /**
17632      * show a specific panel
17633      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17634      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17635      */
17636     showPanel : function (pan)
17637     {
17638         if(this.transition || typeof(pan) == 'undefined'){
17639             Roo.log("waiting for the transitionend");
17640             return;
17641         }
17642         
17643         if (typeof(pan) == 'number') {
17644             pan = this.tabs[pan];
17645         }
17646         
17647         if (typeof(pan) == 'string') {
17648             pan = this.getPanelByName(pan);
17649         }
17650         
17651         var cur = this.getActivePanel();
17652         
17653         if(!pan || !cur){
17654             Roo.log('pan or acitve pan is undefined');
17655             return false;
17656         }
17657         
17658         if (pan.tabId == this.getActivePanel().tabId) {
17659             return true;
17660         }
17661         
17662         if (false === cur.fireEvent('beforedeactivate')) {
17663             return false;
17664         }
17665         
17666         if(this.bullets > 0 && !Roo.isTouch){
17667             this.setActiveBullet(this.indexOfPanel(pan));
17668         }
17669         
17670         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17671             
17672             this.transition = true;
17673             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17674             var lr = dir == 'next' ? 'left' : 'right';
17675             pan.el.addClass(dir); // or prev
17676             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17677             cur.el.addClass(lr); // or right
17678             pan.el.addClass(lr);
17679             
17680             var _this = this;
17681             cur.el.on('transitionend', function() {
17682                 Roo.log("trans end?");
17683                 
17684                 pan.el.removeClass([lr,dir]);
17685                 pan.setActive(true);
17686                 
17687                 cur.el.removeClass([lr]);
17688                 cur.setActive(false);
17689                 
17690                 _this.transition = false;
17691                 
17692             }, this, { single:  true } );
17693             
17694             return true;
17695         }
17696         
17697         cur.setActive(false);
17698         pan.setActive(true);
17699         
17700         return true;
17701         
17702     },
17703     showPanelNext : function()
17704     {
17705         var i = this.indexOfPanel(this.getActivePanel());
17706         
17707         if (i >= this.tabs.length - 1 && !this.autoslide) {
17708             return;
17709         }
17710         
17711         if (i >= this.tabs.length - 1 && this.autoslide) {
17712             i = -1;
17713         }
17714         
17715         this.showPanel(this.tabs[i+1]);
17716     },
17717     
17718     showPanelPrev : function()
17719     {
17720         var i = this.indexOfPanel(this.getActivePanel());
17721         
17722         if (i  < 1 && !this.autoslide) {
17723             return;
17724         }
17725         
17726         if (i < 1 && this.autoslide) {
17727             i = this.tabs.length;
17728         }
17729         
17730         this.showPanel(this.tabs[i-1]);
17731     },
17732     
17733     
17734     addBullet: function()
17735     {
17736         if(!this.bullets || Roo.isTouch){
17737             return;
17738         }
17739         var ctr = this.el.select('.carousel-bullets',true).first();
17740         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17741         var bullet = ctr.createChild({
17742             cls : 'bullet bullet-' + i
17743         },ctr.dom.lastChild);
17744         
17745         
17746         var _this = this;
17747         
17748         bullet.on('click', (function(e, el, o, ii, t){
17749
17750             e.preventDefault();
17751
17752             this.showPanel(ii);
17753
17754             if(this.autoslide && this.slideFn){
17755                 clearInterval(this.slideFn);
17756                 this.slideFn = window.setInterval(function() {
17757                     _this.showPanelNext();
17758                 }, this.timer);
17759             }
17760
17761         }).createDelegate(this, [i, bullet], true));
17762                 
17763         
17764     },
17765      
17766     setActiveBullet : function(i)
17767     {
17768         if(Roo.isTouch){
17769             return;
17770         }
17771         
17772         Roo.each(this.el.select('.bullet', true).elements, function(el){
17773             el.removeClass('selected');
17774         });
17775
17776         var bullet = this.el.select('.bullet-' + i, true).first();
17777         
17778         if(!bullet){
17779             return;
17780         }
17781         
17782         bullet.addClass('selected');
17783     }
17784     
17785     
17786   
17787 });
17788
17789  
17790
17791  
17792  
17793 Roo.apply(Roo.bootstrap.TabGroup, {
17794     
17795     groups: {},
17796      /**
17797     * register a Navigation Group
17798     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17799     */
17800     register : function(navgrp)
17801     {
17802         this.groups[navgrp.navId] = navgrp;
17803         
17804     },
17805     /**
17806     * fetch a Navigation Group based on the navigation ID
17807     * if one does not exist , it will get created.
17808     * @param {string} the navgroup to add
17809     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17810     */
17811     get: function(navId) {
17812         if (typeof(this.groups[navId]) == 'undefined') {
17813             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17814         }
17815         return this.groups[navId] ;
17816     }
17817     
17818     
17819     
17820 });
17821
17822  /*
17823  * - LGPL
17824  *
17825  * TabPanel
17826  * 
17827  */
17828
17829 /**
17830  * @class Roo.bootstrap.TabPanel
17831  * @extends Roo.bootstrap.Component
17832  * Bootstrap TabPanel class
17833  * @cfg {Boolean} active panel active
17834  * @cfg {String} html panel content
17835  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17836  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17837  * @cfg {String} href click to link..
17838  * 
17839  * 
17840  * @constructor
17841  * Create a new TabPanel
17842  * @param {Object} config The config object
17843  */
17844
17845 Roo.bootstrap.TabPanel = function(config){
17846     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17847     this.addEvents({
17848         /**
17849              * @event changed
17850              * Fires when the active status changes
17851              * @param {Roo.bootstrap.TabPanel} this
17852              * @param {Boolean} state the new state
17853             
17854          */
17855         'changed': true,
17856         /**
17857              * @event beforedeactivate
17858              * Fires before a tab is de-activated - can be used to do validation on a form.
17859              * @param {Roo.bootstrap.TabPanel} this
17860              * @return {Boolean} false if there is an error
17861             
17862          */
17863         'beforedeactivate': true
17864      });
17865     
17866     this.tabId = this.tabId || Roo.id();
17867   
17868 };
17869
17870 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17871     
17872     active: false,
17873     html: false,
17874     tabId: false,
17875     navId : false,
17876     href : '',
17877     
17878     getAutoCreate : function(){
17879         var cfg = {
17880             tag: 'div',
17881             // item is needed for carousel - not sure if it has any effect otherwise
17882             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17883             html: this.html || ''
17884         };
17885         
17886         if(this.active){
17887             cfg.cls += ' active';
17888         }
17889         
17890         if(this.tabId){
17891             cfg.tabId = this.tabId;
17892         }
17893         
17894         
17895         return cfg;
17896     },
17897     
17898     initEvents:  function()
17899     {
17900         var p = this.parent();
17901         
17902         this.navId = this.navId || p.navId;
17903         
17904         if (typeof(this.navId) != 'undefined') {
17905             // not really needed.. but just in case.. parent should be a NavGroup.
17906             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17907             
17908             tg.register(this);
17909             
17910             var i = tg.tabs.length - 1;
17911             
17912             if(this.active && tg.bullets > 0 && i < tg.bullets){
17913                 tg.setActiveBullet(i);
17914             }
17915         }
17916         
17917         this.el.on('click', this.onClick, this);
17918         
17919         if(Roo.isTouch){
17920             this.el.on("touchstart", this.onTouchStart, this);
17921             this.el.on("touchmove", this.onTouchMove, this);
17922             this.el.on("touchend", this.onTouchEnd, this);
17923         }
17924         
17925     },
17926     
17927     onRender : function(ct, position)
17928     {
17929         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17930     },
17931     
17932     setActive : function(state)
17933     {
17934         Roo.log("panel - set active " + this.tabId + "=" + state);
17935         
17936         this.active = state;
17937         if (!state) {
17938             this.el.removeClass('active');
17939             
17940         } else  if (!this.el.hasClass('active')) {
17941             this.el.addClass('active');
17942         }
17943         
17944         this.fireEvent('changed', this, state);
17945     },
17946     
17947     onClick : function(e)
17948     {
17949         e.preventDefault();
17950         
17951         if(!this.href.length){
17952             return;
17953         }
17954         
17955         window.location.href = this.href;
17956     },
17957     
17958     startX : 0,
17959     startY : 0,
17960     endX : 0,
17961     endY : 0,
17962     swiping : false,
17963     
17964     onTouchStart : function(e)
17965     {
17966         this.swiping = false;
17967         
17968         this.startX = e.browserEvent.touches[0].clientX;
17969         this.startY = e.browserEvent.touches[0].clientY;
17970     },
17971     
17972     onTouchMove : function(e)
17973     {
17974         this.swiping = true;
17975         
17976         this.endX = e.browserEvent.touches[0].clientX;
17977         this.endY = e.browserEvent.touches[0].clientY;
17978     },
17979     
17980     onTouchEnd : function(e)
17981     {
17982         if(!this.swiping){
17983             this.onClick(e);
17984             return;
17985         }
17986         
17987         var tabGroup = this.parent();
17988         
17989         if(this.endX > this.startX){ // swiping right
17990             tabGroup.showPanelPrev();
17991             return;
17992         }
17993         
17994         if(this.startX > this.endX){ // swiping left
17995             tabGroup.showPanelNext();
17996             return;
17997         }
17998     }
17999     
18000     
18001 });
18002  
18003
18004  
18005
18006  /*
18007  * - LGPL
18008  *
18009  * DateField
18010  * 
18011  */
18012
18013 /**
18014  * @class Roo.bootstrap.DateField
18015  * @extends Roo.bootstrap.Input
18016  * Bootstrap DateField class
18017  * @cfg {Number} weekStart default 0
18018  * @cfg {String} viewMode default empty, (months|years)
18019  * @cfg {String} minViewMode default empty, (months|years)
18020  * @cfg {Number} startDate default -Infinity
18021  * @cfg {Number} endDate default Infinity
18022  * @cfg {Boolean} todayHighlight default false
18023  * @cfg {Boolean} todayBtn default false
18024  * @cfg {Boolean} calendarWeeks default false
18025  * @cfg {Object} daysOfWeekDisabled default empty
18026  * @cfg {Boolean} singleMode default false (true | false)
18027  * 
18028  * @cfg {Boolean} keyboardNavigation default true
18029  * @cfg {String} language default en
18030  * 
18031  * @constructor
18032  * Create a new DateField
18033  * @param {Object} config The config object
18034  */
18035
18036 Roo.bootstrap.DateField = function(config){
18037     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18038      this.addEvents({
18039             /**
18040              * @event show
18041              * Fires when this field show.
18042              * @param {Roo.bootstrap.DateField} this
18043              * @param {Mixed} date The date value
18044              */
18045             show : true,
18046             /**
18047              * @event show
18048              * Fires when this field hide.
18049              * @param {Roo.bootstrap.DateField} this
18050              * @param {Mixed} date The date value
18051              */
18052             hide : true,
18053             /**
18054              * @event select
18055              * Fires when select a date.
18056              * @param {Roo.bootstrap.DateField} this
18057              * @param {Mixed} date The date value
18058              */
18059             select : true,
18060             /**
18061              * @event beforeselect
18062              * Fires when before select a date.
18063              * @param {Roo.bootstrap.DateField} this
18064              * @param {Mixed} date The date value
18065              */
18066             beforeselect : true
18067         });
18068 };
18069
18070 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18071     
18072     /**
18073      * @cfg {String} format
18074      * The default date format string which can be overriden for localization support.  The format must be
18075      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18076      */
18077     format : "m/d/y",
18078     /**
18079      * @cfg {String} altFormats
18080      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18081      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18082      */
18083     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18084     
18085     weekStart : 0,
18086     
18087     viewMode : '',
18088     
18089     minViewMode : '',
18090     
18091     todayHighlight : false,
18092     
18093     todayBtn: false,
18094     
18095     language: 'en',
18096     
18097     keyboardNavigation: true,
18098     
18099     calendarWeeks: false,
18100     
18101     startDate: -Infinity,
18102     
18103     endDate: Infinity,
18104     
18105     daysOfWeekDisabled: [],
18106     
18107     _events: [],
18108     
18109     singleMode : false,
18110     
18111     UTCDate: function()
18112     {
18113         return new Date(Date.UTC.apply(Date, arguments));
18114     },
18115     
18116     UTCToday: function()
18117     {
18118         var today = new Date();
18119         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18120     },
18121     
18122     getDate: function() {
18123             var d = this.getUTCDate();
18124             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18125     },
18126     
18127     getUTCDate: function() {
18128             return this.date;
18129     },
18130     
18131     setDate: function(d) {
18132             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18133     },
18134     
18135     setUTCDate: function(d) {
18136             this.date = d;
18137             this.setValue(this.formatDate(this.date));
18138     },
18139         
18140     onRender: function(ct, position)
18141     {
18142         
18143         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18144         
18145         this.language = this.language || 'en';
18146         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18147         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18148         
18149         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18150         this.format = this.format || 'm/d/y';
18151         this.isInline = false;
18152         this.isInput = true;
18153         this.component = this.el.select('.add-on', true).first() || false;
18154         this.component = (this.component && this.component.length === 0) ? false : this.component;
18155         this.hasInput = this.component && this.inputEl().length;
18156         
18157         if (typeof(this.minViewMode === 'string')) {
18158             switch (this.minViewMode) {
18159                 case 'months':
18160                     this.minViewMode = 1;
18161                     break;
18162                 case 'years':
18163                     this.minViewMode = 2;
18164                     break;
18165                 default:
18166                     this.minViewMode = 0;
18167                     break;
18168             }
18169         }
18170         
18171         if (typeof(this.viewMode === 'string')) {
18172             switch (this.viewMode) {
18173                 case 'months':
18174                     this.viewMode = 1;
18175                     break;
18176                 case 'years':
18177                     this.viewMode = 2;
18178                     break;
18179                 default:
18180                     this.viewMode = 0;
18181                     break;
18182             }
18183         }
18184                 
18185         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18186         
18187 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18188         
18189         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18190         
18191         this.picker().on('mousedown', this.onMousedown, this);
18192         this.picker().on('click', this.onClick, this);
18193         
18194         this.picker().addClass('datepicker-dropdown');
18195         
18196         this.startViewMode = this.viewMode;
18197         
18198         if(this.singleMode){
18199             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18200                 v.setVisibilityMode(Roo.Element.DISPLAY);
18201                 v.hide();
18202             });
18203             
18204             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18205                 v.setStyle('width', '189px');
18206             });
18207         }
18208         
18209         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18210             if(!this.calendarWeeks){
18211                 v.remove();
18212                 return;
18213             }
18214             
18215             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18216             v.attr('colspan', function(i, val){
18217                 return parseInt(val) + 1;
18218             });
18219         });
18220                         
18221         
18222         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18223         
18224         this.setStartDate(this.startDate);
18225         this.setEndDate(this.endDate);
18226         
18227         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18228         
18229         this.fillDow();
18230         this.fillMonths();
18231         this.update();
18232         this.showMode();
18233         
18234         if(this.isInline) {
18235             this.show();
18236         }
18237     },
18238     
18239     picker : function()
18240     {
18241         return this.pickerEl;
18242 //        return this.el.select('.datepicker', true).first();
18243     },
18244     
18245     fillDow: function()
18246     {
18247         var dowCnt = this.weekStart;
18248         
18249         var dow = {
18250             tag: 'tr',
18251             cn: [
18252                 
18253             ]
18254         };
18255         
18256         if(this.calendarWeeks){
18257             dow.cn.push({
18258                 tag: 'th',
18259                 cls: 'cw',
18260                 html: '&nbsp;'
18261             })
18262         }
18263         
18264         while (dowCnt < this.weekStart + 7) {
18265             dow.cn.push({
18266                 tag: 'th',
18267                 cls: 'dow',
18268                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18269             });
18270         }
18271         
18272         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18273     },
18274     
18275     fillMonths: function()
18276     {    
18277         var i = 0;
18278         var months = this.picker().select('>.datepicker-months td', true).first();
18279         
18280         months.dom.innerHTML = '';
18281         
18282         while (i < 12) {
18283             var month = {
18284                 tag: 'span',
18285                 cls: 'month',
18286                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18287             };
18288             
18289             months.createChild(month);
18290         }
18291         
18292     },
18293     
18294     update: function()
18295     {
18296         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;
18297         
18298         if (this.date < this.startDate) {
18299             this.viewDate = new Date(this.startDate);
18300         } else if (this.date > this.endDate) {
18301             this.viewDate = new Date(this.endDate);
18302         } else {
18303             this.viewDate = new Date(this.date);
18304         }
18305         
18306         this.fill();
18307     },
18308     
18309     fill: function() 
18310     {
18311         var d = new Date(this.viewDate),
18312                 year = d.getUTCFullYear(),
18313                 month = d.getUTCMonth(),
18314                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18315                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18316                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18317                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18318                 currentDate = this.date && this.date.valueOf(),
18319                 today = this.UTCToday();
18320         
18321         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18322         
18323 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18324         
18325 //        this.picker.select('>tfoot th.today').
18326 //                                              .text(dates[this.language].today)
18327 //                                              .toggle(this.todayBtn !== false);
18328     
18329         this.updateNavArrows();
18330         this.fillMonths();
18331                                                 
18332         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18333         
18334         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18335          
18336         prevMonth.setUTCDate(day);
18337         
18338         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18339         
18340         var nextMonth = new Date(prevMonth);
18341         
18342         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18343         
18344         nextMonth = nextMonth.valueOf();
18345         
18346         var fillMonths = false;
18347         
18348         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18349         
18350         while(prevMonth.valueOf() < nextMonth) {
18351             var clsName = '';
18352             
18353             if (prevMonth.getUTCDay() === this.weekStart) {
18354                 if(fillMonths){
18355                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18356                 }
18357                     
18358                 fillMonths = {
18359                     tag: 'tr',
18360                     cn: []
18361                 };
18362                 
18363                 if(this.calendarWeeks){
18364                     // ISO 8601: First week contains first thursday.
18365                     // ISO also states week starts on Monday, but we can be more abstract here.
18366                     var
18367                     // Start of current week: based on weekstart/current date
18368                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18369                     // Thursday of this week
18370                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18371                     // First Thursday of year, year from thursday
18372                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18373                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18374                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18375                     
18376                     fillMonths.cn.push({
18377                         tag: 'td',
18378                         cls: 'cw',
18379                         html: calWeek
18380                     });
18381                 }
18382             }
18383             
18384             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18385                 clsName += ' old';
18386             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18387                 clsName += ' new';
18388             }
18389             if (this.todayHighlight &&
18390                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18391                 prevMonth.getUTCMonth() == today.getMonth() &&
18392                 prevMonth.getUTCDate() == today.getDate()) {
18393                 clsName += ' today';
18394             }
18395             
18396             if (currentDate && prevMonth.valueOf() === currentDate) {
18397                 clsName += ' active';
18398             }
18399             
18400             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18401                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18402                     clsName += ' disabled';
18403             }
18404             
18405             fillMonths.cn.push({
18406                 tag: 'td',
18407                 cls: 'day ' + clsName,
18408                 html: prevMonth.getDate()
18409             });
18410             
18411             prevMonth.setDate(prevMonth.getDate()+1);
18412         }
18413           
18414         var currentYear = this.date && this.date.getUTCFullYear();
18415         var currentMonth = this.date && this.date.getUTCMonth();
18416         
18417         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18418         
18419         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18420             v.removeClass('active');
18421             
18422             if(currentYear === year && k === currentMonth){
18423                 v.addClass('active');
18424             }
18425             
18426             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18427                 v.addClass('disabled');
18428             }
18429             
18430         });
18431         
18432         
18433         year = parseInt(year/10, 10) * 10;
18434         
18435         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18436         
18437         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18438         
18439         year -= 1;
18440         for (var i = -1; i < 11; i++) {
18441             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18442                 tag: 'span',
18443                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18444                 html: year
18445             });
18446             
18447             year += 1;
18448         }
18449     },
18450     
18451     showMode: function(dir) 
18452     {
18453         if (dir) {
18454             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18455         }
18456         
18457         Roo.each(this.picker().select('>div',true).elements, function(v){
18458             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18459             v.hide();
18460         });
18461         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18462     },
18463     
18464     place: function()
18465     {
18466         if(this.isInline) {
18467             return;
18468         }
18469         
18470         this.picker().removeClass(['bottom', 'top']);
18471         
18472         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18473             /*
18474              * place to the top of element!
18475              *
18476              */
18477             
18478             this.picker().addClass('top');
18479             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18480             
18481             return;
18482         }
18483         
18484         this.picker().addClass('bottom');
18485         
18486         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18487     },
18488     
18489     parseDate : function(value)
18490     {
18491         if(!value || value instanceof Date){
18492             return value;
18493         }
18494         var v = Date.parseDate(value, this.format);
18495         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18496             v = Date.parseDate(value, 'Y-m-d');
18497         }
18498         if(!v && this.altFormats){
18499             if(!this.altFormatsArray){
18500                 this.altFormatsArray = this.altFormats.split("|");
18501             }
18502             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18503                 v = Date.parseDate(value, this.altFormatsArray[i]);
18504             }
18505         }
18506         return v;
18507     },
18508     
18509     formatDate : function(date, fmt)
18510     {   
18511         return (!date || !(date instanceof Date)) ?
18512         date : date.dateFormat(fmt || this.format);
18513     },
18514     
18515     onFocus : function()
18516     {
18517         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18518         this.show();
18519     },
18520     
18521     onBlur : function()
18522     {
18523         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18524         
18525         var d = this.inputEl().getValue();
18526         
18527         this.setValue(d);
18528                 
18529         this.hide();
18530     },
18531     
18532     show : function()
18533     {
18534         this.picker().show();
18535         this.update();
18536         this.place();
18537         
18538         this.fireEvent('show', this, this.date);
18539     },
18540     
18541     hide : function()
18542     {
18543         if(this.isInline) {
18544             return;
18545         }
18546         this.picker().hide();
18547         this.viewMode = this.startViewMode;
18548         this.showMode();
18549         
18550         this.fireEvent('hide', this, this.date);
18551         
18552     },
18553     
18554     onMousedown: function(e)
18555     {
18556         e.stopPropagation();
18557         e.preventDefault();
18558     },
18559     
18560     keyup: function(e)
18561     {
18562         Roo.bootstrap.DateField.superclass.keyup.call(this);
18563         this.update();
18564     },
18565
18566     setValue: function(v)
18567     {
18568         if(this.fireEvent('beforeselect', this, v) !== false){
18569             var d = new Date(this.parseDate(v) ).clearTime();
18570         
18571             if(isNaN(d.getTime())){
18572                 this.date = this.viewDate = '';
18573                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18574                 return;
18575             }
18576
18577             v = this.formatDate(d);
18578
18579             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18580
18581             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18582
18583             this.update();
18584
18585             this.fireEvent('select', this, this.date);
18586         }
18587     },
18588     
18589     getValue: function()
18590     {
18591         return this.formatDate(this.date);
18592     },
18593     
18594     fireKey: function(e)
18595     {
18596         if (!this.picker().isVisible()){
18597             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18598                 this.show();
18599             }
18600             return;
18601         }
18602         
18603         var dateChanged = false,
18604         dir, day, month,
18605         newDate, newViewDate;
18606         
18607         switch(e.keyCode){
18608             case 27: // escape
18609                 this.hide();
18610                 e.preventDefault();
18611                 break;
18612             case 37: // left
18613             case 39: // right
18614                 if (!this.keyboardNavigation) {
18615                     break;
18616                 }
18617                 dir = e.keyCode == 37 ? -1 : 1;
18618                 
18619                 if (e.ctrlKey){
18620                     newDate = this.moveYear(this.date, dir);
18621                     newViewDate = this.moveYear(this.viewDate, dir);
18622                 } else if (e.shiftKey){
18623                     newDate = this.moveMonth(this.date, dir);
18624                     newViewDate = this.moveMonth(this.viewDate, dir);
18625                 } else {
18626                     newDate = new Date(this.date);
18627                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18628                     newViewDate = new Date(this.viewDate);
18629                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18630                 }
18631                 if (this.dateWithinRange(newDate)){
18632                     this.date = newDate;
18633                     this.viewDate = newViewDate;
18634                     this.setValue(this.formatDate(this.date));
18635 //                    this.update();
18636                     e.preventDefault();
18637                     dateChanged = true;
18638                 }
18639                 break;
18640             case 38: // up
18641             case 40: // down
18642                 if (!this.keyboardNavigation) {
18643                     break;
18644                 }
18645                 dir = e.keyCode == 38 ? -1 : 1;
18646                 if (e.ctrlKey){
18647                     newDate = this.moveYear(this.date, dir);
18648                     newViewDate = this.moveYear(this.viewDate, dir);
18649                 } else if (e.shiftKey){
18650                     newDate = this.moveMonth(this.date, dir);
18651                     newViewDate = this.moveMonth(this.viewDate, dir);
18652                 } else {
18653                     newDate = new Date(this.date);
18654                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18655                     newViewDate = new Date(this.viewDate);
18656                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18657                 }
18658                 if (this.dateWithinRange(newDate)){
18659                     this.date = newDate;
18660                     this.viewDate = newViewDate;
18661                     this.setValue(this.formatDate(this.date));
18662 //                    this.update();
18663                     e.preventDefault();
18664                     dateChanged = true;
18665                 }
18666                 break;
18667             case 13: // enter
18668                 this.setValue(this.formatDate(this.date));
18669                 this.hide();
18670                 e.preventDefault();
18671                 break;
18672             case 9: // tab
18673                 this.setValue(this.formatDate(this.date));
18674                 this.hide();
18675                 break;
18676             case 16: // shift
18677             case 17: // ctrl
18678             case 18: // alt
18679                 break;
18680             default :
18681                 this.hide();
18682                 
18683         }
18684     },
18685     
18686     
18687     onClick: function(e) 
18688     {
18689         e.stopPropagation();
18690         e.preventDefault();
18691         
18692         var target = e.getTarget();
18693         
18694         if(target.nodeName.toLowerCase() === 'i'){
18695             target = Roo.get(target).dom.parentNode;
18696         }
18697         
18698         var nodeName = target.nodeName;
18699         var className = target.className;
18700         var html = target.innerHTML;
18701         //Roo.log(nodeName);
18702         
18703         switch(nodeName.toLowerCase()) {
18704             case 'th':
18705                 switch(className) {
18706                     case 'switch':
18707                         this.showMode(1);
18708                         break;
18709                     case 'prev':
18710                     case 'next':
18711                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18712                         switch(this.viewMode){
18713                                 case 0:
18714                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18715                                         break;
18716                                 case 1:
18717                                 case 2:
18718                                         this.viewDate = this.moveYear(this.viewDate, dir);
18719                                         break;
18720                         }
18721                         this.fill();
18722                         break;
18723                     case 'today':
18724                         var date = new Date();
18725                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18726 //                        this.fill()
18727                         this.setValue(this.formatDate(this.date));
18728                         
18729                         this.hide();
18730                         break;
18731                 }
18732                 break;
18733             case 'span':
18734                 if (className.indexOf('disabled') < 0) {
18735                     this.viewDate.setUTCDate(1);
18736                     if (className.indexOf('month') > -1) {
18737                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18738                     } else {
18739                         var year = parseInt(html, 10) || 0;
18740                         this.viewDate.setUTCFullYear(year);
18741                         
18742                     }
18743                     
18744                     if(this.singleMode){
18745                         this.setValue(this.formatDate(this.viewDate));
18746                         this.hide();
18747                         return;
18748                     }
18749                     
18750                     this.showMode(-1);
18751                     this.fill();
18752                 }
18753                 break;
18754                 
18755             case 'td':
18756                 //Roo.log(className);
18757                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18758                     var day = parseInt(html, 10) || 1;
18759                     var year = this.viewDate.getUTCFullYear(),
18760                         month = this.viewDate.getUTCMonth();
18761
18762                     if (className.indexOf('old') > -1) {
18763                         if(month === 0 ){
18764                             month = 11;
18765                             year -= 1;
18766                         }else{
18767                             month -= 1;
18768                         }
18769                     } else if (className.indexOf('new') > -1) {
18770                         if (month == 11) {
18771                             month = 0;
18772                             year += 1;
18773                         } else {
18774                             month += 1;
18775                         }
18776                     }
18777                     //Roo.log([year,month,day]);
18778                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18779                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18780 //                    this.fill();
18781                     //Roo.log(this.formatDate(this.date));
18782                     this.setValue(this.formatDate(this.date));
18783                     this.hide();
18784                 }
18785                 break;
18786         }
18787     },
18788     
18789     setStartDate: function(startDate)
18790     {
18791         this.startDate = startDate || -Infinity;
18792         if (this.startDate !== -Infinity) {
18793             this.startDate = this.parseDate(this.startDate);
18794         }
18795         this.update();
18796         this.updateNavArrows();
18797     },
18798
18799     setEndDate: function(endDate)
18800     {
18801         this.endDate = endDate || Infinity;
18802         if (this.endDate !== Infinity) {
18803             this.endDate = this.parseDate(this.endDate);
18804         }
18805         this.update();
18806         this.updateNavArrows();
18807     },
18808     
18809     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18810     {
18811         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18812         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18813             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18814         }
18815         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18816             return parseInt(d, 10);
18817         });
18818         this.update();
18819         this.updateNavArrows();
18820     },
18821     
18822     updateNavArrows: function() 
18823     {
18824         if(this.singleMode){
18825             return;
18826         }
18827         
18828         var d = new Date(this.viewDate),
18829         year = d.getUTCFullYear(),
18830         month = d.getUTCMonth();
18831         
18832         Roo.each(this.picker().select('.prev', true).elements, function(v){
18833             v.show();
18834             switch (this.viewMode) {
18835                 case 0:
18836
18837                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18838                         v.hide();
18839                     }
18840                     break;
18841                 case 1:
18842                 case 2:
18843                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18844                         v.hide();
18845                     }
18846                     break;
18847             }
18848         });
18849         
18850         Roo.each(this.picker().select('.next', true).elements, function(v){
18851             v.show();
18852             switch (this.viewMode) {
18853                 case 0:
18854
18855                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18856                         v.hide();
18857                     }
18858                     break;
18859                 case 1:
18860                 case 2:
18861                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18862                         v.hide();
18863                     }
18864                     break;
18865             }
18866         })
18867     },
18868     
18869     moveMonth: function(date, dir)
18870     {
18871         if (!dir) {
18872             return date;
18873         }
18874         var new_date = new Date(date.valueOf()),
18875         day = new_date.getUTCDate(),
18876         month = new_date.getUTCMonth(),
18877         mag = Math.abs(dir),
18878         new_month, test;
18879         dir = dir > 0 ? 1 : -1;
18880         if (mag == 1){
18881             test = dir == -1
18882             // If going back one month, make sure month is not current month
18883             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18884             ? function(){
18885                 return new_date.getUTCMonth() == month;
18886             }
18887             // If going forward one month, make sure month is as expected
18888             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18889             : function(){
18890                 return new_date.getUTCMonth() != new_month;
18891             };
18892             new_month = month + dir;
18893             new_date.setUTCMonth(new_month);
18894             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18895             if (new_month < 0 || new_month > 11) {
18896                 new_month = (new_month + 12) % 12;
18897             }
18898         } else {
18899             // For magnitudes >1, move one month at a time...
18900             for (var i=0; i<mag; i++) {
18901                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18902                 new_date = this.moveMonth(new_date, dir);
18903             }
18904             // ...then reset the day, keeping it in the new month
18905             new_month = new_date.getUTCMonth();
18906             new_date.setUTCDate(day);
18907             test = function(){
18908                 return new_month != new_date.getUTCMonth();
18909             };
18910         }
18911         // Common date-resetting loop -- if date is beyond end of month, make it
18912         // end of month
18913         while (test()){
18914             new_date.setUTCDate(--day);
18915             new_date.setUTCMonth(new_month);
18916         }
18917         return new_date;
18918     },
18919
18920     moveYear: function(date, dir)
18921     {
18922         return this.moveMonth(date, dir*12);
18923     },
18924
18925     dateWithinRange: function(date)
18926     {
18927         return date >= this.startDate && date <= this.endDate;
18928     },
18929
18930     
18931     remove: function() 
18932     {
18933         this.picker().remove();
18934     },
18935     
18936     validateValue : function(value)
18937     {
18938         if(value.length < 1)  {
18939             if(this.allowBlank){
18940                 return true;
18941             }
18942             return false;
18943         }
18944         
18945         if(value.length < this.minLength){
18946             return false;
18947         }
18948         if(value.length > this.maxLength){
18949             return false;
18950         }
18951         if(this.vtype){
18952             var vt = Roo.form.VTypes;
18953             if(!vt[this.vtype](value, this)){
18954                 return false;
18955             }
18956         }
18957         if(typeof this.validator == "function"){
18958             var msg = this.validator(value);
18959             if(msg !== true){
18960                 return false;
18961             }
18962         }
18963         
18964         if(this.regex && !this.regex.test(value)){
18965             return false;
18966         }
18967         
18968         if(typeof(this.parseDate(value)) == 'undefined'){
18969             return false;
18970         }
18971         
18972         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18973             return false;
18974         }      
18975         
18976         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18977             return false;
18978         } 
18979         
18980         
18981         return true;
18982     }
18983    
18984 });
18985
18986 Roo.apply(Roo.bootstrap.DateField,  {
18987     
18988     head : {
18989         tag: 'thead',
18990         cn: [
18991         {
18992             tag: 'tr',
18993             cn: [
18994             {
18995                 tag: 'th',
18996                 cls: 'prev',
18997                 html: '<i class="fa fa-arrow-left"/>'
18998             },
18999             {
19000                 tag: 'th',
19001                 cls: 'switch',
19002                 colspan: '5'
19003             },
19004             {
19005                 tag: 'th',
19006                 cls: 'next',
19007                 html: '<i class="fa fa-arrow-right"/>'
19008             }
19009
19010             ]
19011         }
19012         ]
19013     },
19014     
19015     content : {
19016         tag: 'tbody',
19017         cn: [
19018         {
19019             tag: 'tr',
19020             cn: [
19021             {
19022                 tag: 'td',
19023                 colspan: '7'
19024             }
19025             ]
19026         }
19027         ]
19028     },
19029     
19030     footer : {
19031         tag: 'tfoot',
19032         cn: [
19033         {
19034             tag: 'tr',
19035             cn: [
19036             {
19037                 tag: 'th',
19038                 colspan: '7',
19039                 cls: 'today'
19040             }
19041                     
19042             ]
19043         }
19044         ]
19045     },
19046     
19047     dates:{
19048         en: {
19049             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19050             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19051             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19052             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19053             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19054             today: "Today"
19055         }
19056     },
19057     
19058     modes: [
19059     {
19060         clsName: 'days',
19061         navFnc: 'Month',
19062         navStep: 1
19063     },
19064     {
19065         clsName: 'months',
19066         navFnc: 'FullYear',
19067         navStep: 1
19068     },
19069     {
19070         clsName: 'years',
19071         navFnc: 'FullYear',
19072         navStep: 10
19073     }]
19074 });
19075
19076 Roo.apply(Roo.bootstrap.DateField,  {
19077   
19078     template : {
19079         tag: 'div',
19080         cls: 'datepicker dropdown-menu roo-dynamic',
19081         cn: [
19082         {
19083             tag: 'div',
19084             cls: 'datepicker-days',
19085             cn: [
19086             {
19087                 tag: 'table',
19088                 cls: 'table-condensed',
19089                 cn:[
19090                 Roo.bootstrap.DateField.head,
19091                 {
19092                     tag: 'tbody'
19093                 },
19094                 Roo.bootstrap.DateField.footer
19095                 ]
19096             }
19097             ]
19098         },
19099         {
19100             tag: 'div',
19101             cls: 'datepicker-months',
19102             cn: [
19103             {
19104                 tag: 'table',
19105                 cls: 'table-condensed',
19106                 cn:[
19107                 Roo.bootstrap.DateField.head,
19108                 Roo.bootstrap.DateField.content,
19109                 Roo.bootstrap.DateField.footer
19110                 ]
19111             }
19112             ]
19113         },
19114         {
19115             tag: 'div',
19116             cls: 'datepicker-years',
19117             cn: [
19118             {
19119                 tag: 'table',
19120                 cls: 'table-condensed',
19121                 cn:[
19122                 Roo.bootstrap.DateField.head,
19123                 Roo.bootstrap.DateField.content,
19124                 Roo.bootstrap.DateField.footer
19125                 ]
19126             }
19127             ]
19128         }
19129         ]
19130     }
19131 });
19132
19133  
19134
19135  /*
19136  * - LGPL
19137  *
19138  * TimeField
19139  * 
19140  */
19141
19142 /**
19143  * @class Roo.bootstrap.TimeField
19144  * @extends Roo.bootstrap.Input
19145  * Bootstrap DateField class
19146  * 
19147  * 
19148  * @constructor
19149  * Create a new TimeField
19150  * @param {Object} config The config object
19151  */
19152
19153 Roo.bootstrap.TimeField = function(config){
19154     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19155     this.addEvents({
19156             /**
19157              * @event show
19158              * Fires when this field show.
19159              * @param {Roo.bootstrap.DateField} thisthis
19160              * @param {Mixed} date The date value
19161              */
19162             show : true,
19163             /**
19164              * @event show
19165              * Fires when this field hide.
19166              * @param {Roo.bootstrap.DateField} this
19167              * @param {Mixed} date The date value
19168              */
19169             hide : true,
19170             /**
19171              * @event select
19172              * Fires when select a date.
19173              * @param {Roo.bootstrap.DateField} this
19174              * @param {Mixed} date The date value
19175              */
19176             select : true
19177         });
19178 };
19179
19180 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19181     
19182     /**
19183      * @cfg {String} format
19184      * The default time format string which can be overriden for localization support.  The format must be
19185      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19186      */
19187     format : "H:i",
19188        
19189     onRender: function(ct, position)
19190     {
19191         
19192         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19193                 
19194         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19195         
19196         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19197         
19198         this.pop = this.picker().select('>.datepicker-time',true).first();
19199         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19200         
19201         this.picker().on('mousedown', this.onMousedown, this);
19202         this.picker().on('click', this.onClick, this);
19203         
19204         this.picker().addClass('datepicker-dropdown');
19205     
19206         this.fillTime();
19207         this.update();
19208             
19209         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19210         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19211         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19212         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19213         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19214         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19215
19216     },
19217     
19218     fireKey: function(e){
19219         if (!this.picker().isVisible()){
19220             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19221                 this.show();
19222             }
19223             return;
19224         }
19225
19226         e.preventDefault();
19227         
19228         switch(e.keyCode){
19229             case 27: // escape
19230                 this.hide();
19231                 break;
19232             case 37: // left
19233             case 39: // right
19234                 this.onTogglePeriod();
19235                 break;
19236             case 38: // up
19237                 this.onIncrementMinutes();
19238                 break;
19239             case 40: // down
19240                 this.onDecrementMinutes();
19241                 break;
19242             case 13: // enter
19243             case 9: // tab
19244                 this.setTime();
19245                 break;
19246         }
19247     },
19248     
19249     onClick: function(e) {
19250         e.stopPropagation();
19251         e.preventDefault();
19252     },
19253     
19254     picker : function()
19255     {
19256         return this.el.select('.datepicker', true).first();
19257     },
19258     
19259     fillTime: function()
19260     {    
19261         var time = this.pop.select('tbody', true).first();
19262         
19263         time.dom.innerHTML = '';
19264         
19265         time.createChild({
19266             tag: 'tr',
19267             cn: [
19268                 {
19269                     tag: 'td',
19270                     cn: [
19271                         {
19272                             tag: 'a',
19273                             href: '#',
19274                             cls: 'btn',
19275                             cn: [
19276                                 {
19277                                     tag: 'span',
19278                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19279                                 }
19280                             ]
19281                         } 
19282                     ]
19283                 },
19284                 {
19285                     tag: 'td',
19286                     cls: 'separator'
19287                 },
19288                 {
19289                     tag: 'td',
19290                     cn: [
19291                         {
19292                             tag: 'a',
19293                             href: '#',
19294                             cls: 'btn',
19295                             cn: [
19296                                 {
19297                                     tag: 'span',
19298                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19299                                 }
19300                             ]
19301                         }
19302                     ]
19303                 },
19304                 {
19305                     tag: 'td',
19306                     cls: 'separator'
19307                 }
19308             ]
19309         });
19310         
19311         time.createChild({
19312             tag: 'tr',
19313             cn: [
19314                 {
19315                     tag: 'td',
19316                     cn: [
19317                         {
19318                             tag: 'span',
19319                             cls: 'timepicker-hour',
19320                             html: '00'
19321                         }  
19322                     ]
19323                 },
19324                 {
19325                     tag: 'td',
19326                     cls: 'separator',
19327                     html: ':'
19328                 },
19329                 {
19330                     tag: 'td',
19331                     cn: [
19332                         {
19333                             tag: 'span',
19334                             cls: 'timepicker-minute',
19335                             html: '00'
19336                         }  
19337                     ]
19338                 },
19339                 {
19340                     tag: 'td',
19341                     cls: 'separator'
19342                 },
19343                 {
19344                     tag: 'td',
19345                     cn: [
19346                         {
19347                             tag: 'button',
19348                             type: 'button',
19349                             cls: 'btn btn-primary period',
19350                             html: 'AM'
19351                             
19352                         }
19353                     ]
19354                 }
19355             ]
19356         });
19357         
19358         time.createChild({
19359             tag: 'tr',
19360             cn: [
19361                 {
19362                     tag: 'td',
19363                     cn: [
19364                         {
19365                             tag: 'a',
19366                             href: '#',
19367                             cls: 'btn',
19368                             cn: [
19369                                 {
19370                                     tag: 'span',
19371                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19372                                 }
19373                             ]
19374                         }
19375                     ]
19376                 },
19377                 {
19378                     tag: 'td',
19379                     cls: 'separator'
19380                 },
19381                 {
19382                     tag: 'td',
19383                     cn: [
19384                         {
19385                             tag: 'a',
19386                             href: '#',
19387                             cls: 'btn',
19388                             cn: [
19389                                 {
19390                                     tag: 'span',
19391                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19392                                 }
19393                             ]
19394                         }
19395                     ]
19396                 },
19397                 {
19398                     tag: 'td',
19399                     cls: 'separator'
19400                 }
19401             ]
19402         });
19403         
19404     },
19405     
19406     update: function()
19407     {
19408         
19409         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19410         
19411         this.fill();
19412     },
19413     
19414     fill: function() 
19415     {
19416         var hours = this.time.getHours();
19417         var minutes = this.time.getMinutes();
19418         var period = 'AM';
19419         
19420         if(hours > 11){
19421             period = 'PM';
19422         }
19423         
19424         if(hours == 0){
19425             hours = 12;
19426         }
19427         
19428         
19429         if(hours > 12){
19430             hours = hours - 12;
19431         }
19432         
19433         if(hours < 10){
19434             hours = '0' + hours;
19435         }
19436         
19437         if(minutes < 10){
19438             minutes = '0' + minutes;
19439         }
19440         
19441         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19442         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19443         this.pop.select('button', true).first().dom.innerHTML = period;
19444         
19445     },
19446     
19447     place: function()
19448     {   
19449         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19450         
19451         var cls = ['bottom'];
19452         
19453         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19454             cls.pop();
19455             cls.push('top');
19456         }
19457         
19458         cls.push('right');
19459         
19460         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19461             cls.pop();
19462             cls.push('left');
19463         }
19464         
19465         this.picker().addClass(cls.join('-'));
19466         
19467         var _this = this;
19468         
19469         Roo.each(cls, function(c){
19470             if(c == 'bottom'){
19471                 _this.picker().setTop(_this.inputEl().getHeight());
19472                 return;
19473             }
19474             if(c == 'top'){
19475                 _this.picker().setTop(0 - _this.picker().getHeight());
19476                 return;
19477             }
19478             
19479             if(c == 'left'){
19480                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19481                 return;
19482             }
19483             if(c == 'right'){
19484                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19485                 return;
19486             }
19487         });
19488         
19489     },
19490   
19491     onFocus : function()
19492     {
19493         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19494         this.show();
19495     },
19496     
19497     onBlur : function()
19498     {
19499         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19500         this.hide();
19501     },
19502     
19503     show : function()
19504     {
19505         this.picker().show();
19506         this.pop.show();
19507         this.update();
19508         this.place();
19509         
19510         this.fireEvent('show', this, this.date);
19511     },
19512     
19513     hide : function()
19514     {
19515         this.picker().hide();
19516         this.pop.hide();
19517         
19518         this.fireEvent('hide', this, this.date);
19519     },
19520     
19521     setTime : function()
19522     {
19523         this.hide();
19524         this.setValue(this.time.format(this.format));
19525         
19526         this.fireEvent('select', this, this.date);
19527         
19528         
19529     },
19530     
19531     onMousedown: function(e){
19532         e.stopPropagation();
19533         e.preventDefault();
19534     },
19535     
19536     onIncrementHours: function()
19537     {
19538         Roo.log('onIncrementHours');
19539         this.time = this.time.add(Date.HOUR, 1);
19540         this.update();
19541         
19542     },
19543     
19544     onDecrementHours: function()
19545     {
19546         Roo.log('onDecrementHours');
19547         this.time = this.time.add(Date.HOUR, -1);
19548         this.update();
19549     },
19550     
19551     onIncrementMinutes: function()
19552     {
19553         Roo.log('onIncrementMinutes');
19554         this.time = this.time.add(Date.MINUTE, 1);
19555         this.update();
19556     },
19557     
19558     onDecrementMinutes: function()
19559     {
19560         Roo.log('onDecrementMinutes');
19561         this.time = this.time.add(Date.MINUTE, -1);
19562         this.update();
19563     },
19564     
19565     onTogglePeriod: function()
19566     {
19567         Roo.log('onTogglePeriod');
19568         this.time = this.time.add(Date.HOUR, 12);
19569         this.update();
19570     }
19571     
19572    
19573 });
19574
19575 Roo.apply(Roo.bootstrap.TimeField,  {
19576     
19577     content : {
19578         tag: 'tbody',
19579         cn: [
19580             {
19581                 tag: 'tr',
19582                 cn: [
19583                 {
19584                     tag: 'td',
19585                     colspan: '7'
19586                 }
19587                 ]
19588             }
19589         ]
19590     },
19591     
19592     footer : {
19593         tag: 'tfoot',
19594         cn: [
19595             {
19596                 tag: 'tr',
19597                 cn: [
19598                 {
19599                     tag: 'th',
19600                     colspan: '7',
19601                     cls: '',
19602                     cn: [
19603                         {
19604                             tag: 'button',
19605                             cls: 'btn btn-info ok',
19606                             html: 'OK'
19607                         }
19608                     ]
19609                 }
19610
19611                 ]
19612             }
19613         ]
19614     }
19615 });
19616
19617 Roo.apply(Roo.bootstrap.TimeField,  {
19618   
19619     template : {
19620         tag: 'div',
19621         cls: 'datepicker dropdown-menu',
19622         cn: [
19623             {
19624                 tag: 'div',
19625                 cls: 'datepicker-time',
19626                 cn: [
19627                 {
19628                     tag: 'table',
19629                     cls: 'table-condensed',
19630                     cn:[
19631                     Roo.bootstrap.TimeField.content,
19632                     Roo.bootstrap.TimeField.footer
19633                     ]
19634                 }
19635                 ]
19636             }
19637         ]
19638     }
19639 });
19640
19641  
19642
19643  /*
19644  * - LGPL
19645  *
19646  * MonthField
19647  * 
19648  */
19649
19650 /**
19651  * @class Roo.bootstrap.MonthField
19652  * @extends Roo.bootstrap.Input
19653  * Bootstrap MonthField class
19654  * 
19655  * @cfg {String} language default en
19656  * 
19657  * @constructor
19658  * Create a new MonthField
19659  * @param {Object} config The config object
19660  */
19661
19662 Roo.bootstrap.MonthField = function(config){
19663     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19664     
19665     this.addEvents({
19666         /**
19667          * @event show
19668          * Fires when this field show.
19669          * @param {Roo.bootstrap.MonthField} this
19670          * @param {Mixed} date The date value
19671          */
19672         show : true,
19673         /**
19674          * @event show
19675          * Fires when this field hide.
19676          * @param {Roo.bootstrap.MonthField} this
19677          * @param {Mixed} date The date value
19678          */
19679         hide : true,
19680         /**
19681          * @event select
19682          * Fires when select a date.
19683          * @param {Roo.bootstrap.MonthField} this
19684          * @param {String} oldvalue The old value
19685          * @param {String} newvalue The new value
19686          */
19687         select : true
19688     });
19689 };
19690
19691 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19692     
19693     onRender: function(ct, position)
19694     {
19695         
19696         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19697         
19698         this.language = this.language || 'en';
19699         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19700         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19701         
19702         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19703         this.isInline = false;
19704         this.isInput = true;
19705         this.component = this.el.select('.add-on', true).first() || false;
19706         this.component = (this.component && this.component.length === 0) ? false : this.component;
19707         this.hasInput = this.component && this.inputEL().length;
19708         
19709         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19710         
19711         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19712         
19713         this.picker().on('mousedown', this.onMousedown, this);
19714         this.picker().on('click', this.onClick, this);
19715         
19716         this.picker().addClass('datepicker-dropdown');
19717         
19718         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19719             v.setStyle('width', '189px');
19720         });
19721         
19722         this.fillMonths();
19723         
19724         this.update();
19725         
19726         if(this.isInline) {
19727             this.show();
19728         }
19729         
19730     },
19731     
19732     setValue: function(v, suppressEvent)
19733     {   
19734         var o = this.getValue();
19735         
19736         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19737         
19738         this.update();
19739
19740         if(suppressEvent !== true){
19741             this.fireEvent('select', this, o, v);
19742         }
19743         
19744     },
19745     
19746     getValue: function()
19747     {
19748         return this.value;
19749     },
19750     
19751     onClick: function(e) 
19752     {
19753         e.stopPropagation();
19754         e.preventDefault();
19755         
19756         var target = e.getTarget();
19757         
19758         if(target.nodeName.toLowerCase() === 'i'){
19759             target = Roo.get(target).dom.parentNode;
19760         }
19761         
19762         var nodeName = target.nodeName;
19763         var className = target.className;
19764         var html = target.innerHTML;
19765         
19766         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19767             return;
19768         }
19769         
19770         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19771         
19772         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19773         
19774         this.hide();
19775                         
19776     },
19777     
19778     picker : function()
19779     {
19780         return this.pickerEl;
19781     },
19782     
19783     fillMonths: function()
19784     {    
19785         var i = 0;
19786         var months = this.picker().select('>.datepicker-months td', true).first();
19787         
19788         months.dom.innerHTML = '';
19789         
19790         while (i < 12) {
19791             var month = {
19792                 tag: 'span',
19793                 cls: 'month',
19794                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19795             };
19796             
19797             months.createChild(month);
19798         }
19799         
19800     },
19801     
19802     update: function()
19803     {
19804         var _this = this;
19805         
19806         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19807             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19808         }
19809         
19810         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19811             e.removeClass('active');
19812             
19813             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19814                 e.addClass('active');
19815             }
19816         })
19817     },
19818     
19819     place: function()
19820     {
19821         if(this.isInline) {
19822             return;
19823         }
19824         
19825         this.picker().removeClass(['bottom', 'top']);
19826         
19827         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19828             /*
19829              * place to the top of element!
19830              *
19831              */
19832             
19833             this.picker().addClass('top');
19834             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19835             
19836             return;
19837         }
19838         
19839         this.picker().addClass('bottom');
19840         
19841         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19842     },
19843     
19844     onFocus : function()
19845     {
19846         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19847         this.show();
19848     },
19849     
19850     onBlur : function()
19851     {
19852         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19853         
19854         var d = this.inputEl().getValue();
19855         
19856         this.setValue(d);
19857                 
19858         this.hide();
19859     },
19860     
19861     show : function()
19862     {
19863         this.picker().show();
19864         this.picker().select('>.datepicker-months', true).first().show();
19865         this.update();
19866         this.place();
19867         
19868         this.fireEvent('show', this, this.date);
19869     },
19870     
19871     hide : function()
19872     {
19873         if(this.isInline) {
19874             return;
19875         }
19876         this.picker().hide();
19877         this.fireEvent('hide', this, this.date);
19878         
19879     },
19880     
19881     onMousedown: function(e)
19882     {
19883         e.stopPropagation();
19884         e.preventDefault();
19885     },
19886     
19887     keyup: function(e)
19888     {
19889         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19890         this.update();
19891     },
19892
19893     fireKey: function(e)
19894     {
19895         if (!this.picker().isVisible()){
19896             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19897                 this.show();
19898             }
19899             return;
19900         }
19901         
19902         var dir;
19903         
19904         switch(e.keyCode){
19905             case 27: // escape
19906                 this.hide();
19907                 e.preventDefault();
19908                 break;
19909             case 37: // left
19910             case 39: // right
19911                 dir = e.keyCode == 37 ? -1 : 1;
19912                 
19913                 this.vIndex = this.vIndex + dir;
19914                 
19915                 if(this.vIndex < 0){
19916                     this.vIndex = 0;
19917                 }
19918                 
19919                 if(this.vIndex > 11){
19920                     this.vIndex = 11;
19921                 }
19922                 
19923                 if(isNaN(this.vIndex)){
19924                     this.vIndex = 0;
19925                 }
19926                 
19927                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19928                 
19929                 break;
19930             case 38: // up
19931             case 40: // down
19932                 
19933                 dir = e.keyCode == 38 ? -1 : 1;
19934                 
19935                 this.vIndex = this.vIndex + dir * 4;
19936                 
19937                 if(this.vIndex < 0){
19938                     this.vIndex = 0;
19939                 }
19940                 
19941                 if(this.vIndex > 11){
19942                     this.vIndex = 11;
19943                 }
19944                 
19945                 if(isNaN(this.vIndex)){
19946                     this.vIndex = 0;
19947                 }
19948                 
19949                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19950                 break;
19951                 
19952             case 13: // enter
19953                 
19954                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19955                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19956                 }
19957                 
19958                 this.hide();
19959                 e.preventDefault();
19960                 break;
19961             case 9: // tab
19962                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19963                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19964                 }
19965                 this.hide();
19966                 break;
19967             case 16: // shift
19968             case 17: // ctrl
19969             case 18: // alt
19970                 break;
19971             default :
19972                 this.hide();
19973                 
19974         }
19975     },
19976     
19977     remove: function() 
19978     {
19979         this.picker().remove();
19980     }
19981    
19982 });
19983
19984 Roo.apply(Roo.bootstrap.MonthField,  {
19985     
19986     content : {
19987         tag: 'tbody',
19988         cn: [
19989         {
19990             tag: 'tr',
19991             cn: [
19992             {
19993                 tag: 'td',
19994                 colspan: '7'
19995             }
19996             ]
19997         }
19998         ]
19999     },
20000     
20001     dates:{
20002         en: {
20003             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20004             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20005         }
20006     }
20007 });
20008
20009 Roo.apply(Roo.bootstrap.MonthField,  {
20010   
20011     template : {
20012         tag: 'div',
20013         cls: 'datepicker dropdown-menu roo-dynamic',
20014         cn: [
20015             {
20016                 tag: 'div',
20017                 cls: 'datepicker-months',
20018                 cn: [
20019                 {
20020                     tag: 'table',
20021                     cls: 'table-condensed',
20022                     cn:[
20023                         Roo.bootstrap.DateField.content
20024                     ]
20025                 }
20026                 ]
20027             }
20028         ]
20029     }
20030 });
20031
20032  
20033
20034  
20035  /*
20036  * - LGPL
20037  *
20038  * CheckBox
20039  * 
20040  */
20041
20042 /**
20043  * @class Roo.bootstrap.CheckBox
20044  * @extends Roo.bootstrap.Input
20045  * Bootstrap CheckBox class
20046  * 
20047  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20048  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20049  * @cfg {String} boxLabel The text that appears beside the checkbox
20050  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20051  * @cfg {Boolean} checked initnal the element
20052  * @cfg {Boolean} inline inline the element (default false)
20053  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20054  * 
20055  * @constructor
20056  * Create a new CheckBox
20057  * @param {Object} config The config object
20058  */
20059
20060 Roo.bootstrap.CheckBox = function(config){
20061     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20062    
20063     this.addEvents({
20064         /**
20065         * @event check
20066         * Fires when the element is checked or unchecked.
20067         * @param {Roo.bootstrap.CheckBox} this This input
20068         * @param {Boolean} checked The new checked value
20069         */
20070        check : true
20071     });
20072     
20073 };
20074
20075 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20076   
20077     inputType: 'checkbox',
20078     inputValue: 1,
20079     valueOff: 0,
20080     boxLabel: false,
20081     checked: false,
20082     weight : false,
20083     inline: false,
20084     
20085     getAutoCreate : function()
20086     {
20087         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20088         
20089         var id = Roo.id();
20090         
20091         var cfg = {};
20092         
20093         cfg.cls = 'form-group ' + this.inputType; //input-group
20094         
20095         if(this.inline){
20096             cfg.cls += ' ' + this.inputType + '-inline';
20097         }
20098         
20099         var input =  {
20100             tag: 'input',
20101             id : id,
20102             type : this.inputType,
20103             value : this.inputValue,
20104             cls : 'roo-' + this.inputType, //'form-box',
20105             placeholder : this.placeholder || ''
20106             
20107         };
20108         
20109         if(this.inputType != 'radio'){
20110             var hidden =  {
20111                 tag: 'input',
20112                 type : 'hidden',
20113                 cls : 'roo-hidden-value',
20114                 value : this.checked ? this.valueOff : this.inputValue
20115             };
20116         }
20117         
20118             
20119         if (this.weight) { // Validity check?
20120             cfg.cls += " " + this.inputType + "-" + this.weight;
20121         }
20122         
20123         if (this.disabled) {
20124             input.disabled=true;
20125         }
20126         
20127         if(this.checked){
20128             input.checked = this.checked;
20129             
20130         }
20131         
20132         
20133         if (this.name) {
20134             
20135             input.name = this.name;
20136             
20137             if(this.inputType != 'radio'){
20138                 hidden.name = this.name;
20139                 input.name = '_hidden_' + this.name;
20140             }
20141         }
20142         
20143         if (this.size) {
20144             input.cls += ' input-' + this.size;
20145         }
20146         
20147         var settings=this;
20148         
20149         ['xs','sm','md','lg'].map(function(size){
20150             if (settings[size]) {
20151                 cfg.cls += ' col-' + size + '-' + settings[size];
20152             }
20153         });
20154         
20155         var inputblock = input;
20156          
20157         if (this.before || this.after) {
20158             
20159             inputblock = {
20160                 cls : 'input-group',
20161                 cn :  [] 
20162             };
20163             
20164             if (this.before) {
20165                 inputblock.cn.push({
20166                     tag :'span',
20167                     cls : 'input-group-addon',
20168                     html : this.before
20169                 });
20170             }
20171             
20172             inputblock.cn.push(input);
20173             
20174             if(this.inputType != 'radio'){
20175                 inputblock.cn.push(hidden);
20176             }
20177             
20178             if (this.after) {
20179                 inputblock.cn.push({
20180                     tag :'span',
20181                     cls : 'input-group-addon',
20182                     html : this.after
20183                 });
20184             }
20185             
20186         }
20187         
20188         if (align ==='left' && this.fieldLabel.length) {
20189 //                Roo.log("left and has label");
20190             cfg.cn = [
20191                 {
20192                     tag: 'label',
20193                     'for' :  id,
20194                     cls : 'control-label',
20195                     html : this.fieldLabel
20196
20197                 },
20198                 {
20199                     cls : "", 
20200                     cn: [
20201                         inputblock
20202                     ]
20203                 }
20204             ];
20205             
20206             if(this.labelWidth > 12){
20207                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20208             }
20209             
20210             if(this.labelWidth < 13 && this.labelmd == 0){
20211                 this.labelmd = this.labelWidth;
20212             }
20213             
20214             if(this.labellg > 0){
20215                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20216                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20217             }
20218             
20219             if(this.labelmd > 0){
20220                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20221                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20222             }
20223             
20224             if(this.labelsm > 0){
20225                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20226                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20227             }
20228             
20229             if(this.labelxs > 0){
20230                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20231                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20232             }
20233             
20234         } else if ( this.fieldLabel.length) {
20235 //                Roo.log(" label");
20236                 cfg.cn = [
20237                    
20238                     {
20239                         tag: this.boxLabel ? 'span' : 'label',
20240                         'for': id,
20241                         cls: 'control-label box-input-label',
20242                         //cls : 'input-group-addon',
20243                         html : this.fieldLabel
20244                         
20245                     },
20246                     
20247                     inputblock
20248                     
20249                 ];
20250
20251         } else {
20252             
20253 //                Roo.log(" no label && no align");
20254                 cfg.cn = [  inputblock ] ;
20255                 
20256                 
20257         }
20258         
20259         if(this.boxLabel){
20260              var boxLabelCfg = {
20261                 tag: 'label',
20262                 //'for': id, // box label is handled by onclick - so no for...
20263                 cls: 'box-label',
20264                 html: this.boxLabel
20265             };
20266             
20267             if(this.tooltip){
20268                 boxLabelCfg.tooltip = this.tooltip;
20269             }
20270              
20271             cfg.cn.push(boxLabelCfg);
20272         }
20273         
20274         if(this.inputType != 'radio'){
20275             cfg.cn.push(hidden);
20276         }
20277         
20278         return cfg;
20279         
20280     },
20281     
20282     /**
20283      * return the real input element.
20284      */
20285     inputEl: function ()
20286     {
20287         return this.el.select('input.roo-' + this.inputType,true).first();
20288     },
20289     hiddenEl: function ()
20290     {
20291         return this.el.select('input.roo-hidden-value',true).first();
20292     },
20293     
20294     labelEl: function()
20295     {
20296         return this.el.select('label.control-label',true).first();
20297     },
20298     /* depricated... */
20299     
20300     label: function()
20301     {
20302         return this.labelEl();
20303     },
20304     
20305     boxLabelEl: function()
20306     {
20307         return this.el.select('label.box-label',true).first();
20308     },
20309     
20310     initEvents : function()
20311     {
20312 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20313         
20314         this.inputEl().on('click', this.onClick,  this);
20315         
20316         if (this.boxLabel) { 
20317             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20318         }
20319         
20320         this.startValue = this.getValue();
20321         
20322         if(this.groupId){
20323             Roo.bootstrap.CheckBox.register(this);
20324         }
20325     },
20326     
20327     onClick : function()
20328     {   
20329         this.setChecked(!this.checked);
20330     },
20331     
20332     setChecked : function(state,suppressEvent)
20333     {
20334         this.startValue = this.getValue();
20335
20336         if(this.inputType == 'radio'){
20337             
20338             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20339                 e.dom.checked = false;
20340             });
20341             
20342             this.inputEl().dom.checked = true;
20343             
20344             this.inputEl().dom.value = this.inputValue;
20345             
20346             if(suppressEvent !== true){
20347                 this.fireEvent('check', this, true);
20348             }
20349             
20350             this.validate();
20351             
20352             return;
20353         }
20354         
20355         this.checked = state;
20356         
20357         this.inputEl().dom.checked = state;
20358         
20359         
20360         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20361         
20362         if(suppressEvent !== true){
20363             this.fireEvent('check', this, state);
20364         }
20365         
20366         this.validate();
20367     },
20368     
20369     getValue : function()
20370     {
20371         if(this.inputType == 'radio'){
20372             return this.getGroupValue();
20373         }
20374         
20375         return this.hiddenEl().dom.value;
20376         
20377     },
20378     
20379     getGroupValue : function()
20380     {
20381         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20382             return '';
20383         }
20384         
20385         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20386     },
20387     
20388     setValue : function(v,suppressEvent)
20389     {
20390         if(this.inputType == 'radio'){
20391             this.setGroupValue(v, suppressEvent);
20392             return;
20393         }
20394         
20395         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20396         
20397         this.validate();
20398     },
20399     
20400     setGroupValue : function(v, suppressEvent)
20401     {
20402         this.startValue = this.getValue();
20403         
20404         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20405             e.dom.checked = false;
20406             
20407             if(e.dom.value == v){
20408                 e.dom.checked = true;
20409             }
20410         });
20411         
20412         if(suppressEvent !== true){
20413             this.fireEvent('check', this, true);
20414         }
20415
20416         this.validate();
20417         
20418         return;
20419     },
20420     
20421     validate : function()
20422     {
20423         if(
20424                 this.disabled || 
20425                 (this.inputType == 'radio' && this.validateRadio()) ||
20426                 (this.inputType == 'checkbox' && this.validateCheckbox())
20427         ){
20428             this.markValid();
20429             return true;
20430         }
20431         
20432         this.markInvalid();
20433         return false;
20434     },
20435     
20436     validateRadio : function()
20437     {
20438         if(this.allowBlank){
20439             return true;
20440         }
20441         
20442         var valid = false;
20443         
20444         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20445             if(!e.dom.checked){
20446                 return;
20447             }
20448             
20449             valid = true;
20450             
20451             return false;
20452         });
20453         
20454         return valid;
20455     },
20456     
20457     validateCheckbox : function()
20458     {
20459         if(!this.groupId){
20460             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20461             //return (this.getValue() == this.inputValue) ? true : false;
20462         }
20463         
20464         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20465         
20466         if(!group){
20467             return false;
20468         }
20469         
20470         var r = false;
20471         
20472         for(var i in group){
20473             if(r){
20474                 break;
20475             }
20476             
20477             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20478         }
20479         
20480         return r;
20481     },
20482     
20483     /**
20484      * Mark this field as valid
20485      */
20486     markValid : function()
20487     {
20488         var _this = this;
20489         
20490         this.fireEvent('valid', this);
20491         
20492         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20493         
20494         if(this.groupId){
20495             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20496         }
20497         
20498         if(label){
20499             label.markValid();
20500         }
20501
20502         if(this.inputType == 'radio'){
20503             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20504                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20505                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20506             });
20507             
20508             return;
20509         }
20510
20511         if(!this.groupId){
20512             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20513             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20514             return;
20515         }
20516         
20517         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20518         
20519         if(!group){
20520             return;
20521         }
20522         
20523         for(var i in group){
20524             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20525             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20526         }
20527     },
20528     
20529      /**
20530      * Mark this field as invalid
20531      * @param {String} msg The validation message
20532      */
20533     markInvalid : function(msg)
20534     {
20535         if(this.allowBlank){
20536             return;
20537         }
20538         
20539         var _this = this;
20540         
20541         this.fireEvent('invalid', this, msg);
20542         
20543         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20544         
20545         if(this.groupId){
20546             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20547         }
20548         
20549         if(label){
20550             label.markInvalid();
20551         }
20552             
20553         if(this.inputType == 'radio'){
20554             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20555                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20556                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20557             });
20558             
20559             return;
20560         }
20561         
20562         if(!this.groupId){
20563             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20564             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20565             return;
20566         }
20567         
20568         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20569         
20570         if(!group){
20571             return;
20572         }
20573         
20574         for(var i in group){
20575             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20576             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20577         }
20578         
20579     },
20580     
20581     clearInvalid : function()
20582     {
20583         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20584         
20585         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20586         
20587         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20588         
20589         if (label) {
20590             label.iconEl.removeClass(label.validClass);
20591             label.iconEl.removeClass(label.invalidClass);
20592         }
20593     },
20594     
20595     disable : function()
20596     {
20597         if(this.inputType != 'radio'){
20598             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20599             return;
20600         }
20601         
20602         var _this = this;
20603         
20604         if(this.rendered){
20605             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20606                 _this.getActionEl().addClass(this.disabledClass);
20607                 e.dom.disabled = true;
20608             });
20609         }
20610         
20611         this.disabled = true;
20612         this.fireEvent("disable", this);
20613         return this;
20614     },
20615
20616     enable : function()
20617     {
20618         if(this.inputType != 'radio'){
20619             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20620             return;
20621         }
20622         
20623         var _this = this;
20624         
20625         if(this.rendered){
20626             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20627                 _this.getActionEl().removeClass(this.disabledClass);
20628                 e.dom.disabled = false;
20629             });
20630         }
20631         
20632         this.disabled = false;
20633         this.fireEvent("enable", this);
20634         return this;
20635     }
20636
20637 });
20638
20639 Roo.apply(Roo.bootstrap.CheckBox, {
20640     
20641     groups: {},
20642     
20643      /**
20644     * register a CheckBox Group
20645     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20646     */
20647     register : function(checkbox)
20648     {
20649         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20650             this.groups[checkbox.groupId] = {};
20651         }
20652         
20653         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20654             return;
20655         }
20656         
20657         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20658         
20659     },
20660     /**
20661     * fetch a CheckBox Group based on the group ID
20662     * @param {string} the group ID
20663     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20664     */
20665     get: function(groupId) {
20666         if (typeof(this.groups[groupId]) == 'undefined') {
20667             return false;
20668         }
20669         
20670         return this.groups[groupId] ;
20671     }
20672     
20673     
20674 });
20675 /*
20676  * - LGPL
20677  *
20678  * RadioItem
20679  * 
20680  */
20681
20682 /**
20683  * @class Roo.bootstrap.Radio
20684  * @extends Roo.bootstrap.Component
20685  * Bootstrap Radio class
20686  * @cfg {String} boxLabel - the label associated
20687  * @cfg {String} value - the value of radio
20688  * 
20689  * @constructor
20690  * Create a new Radio
20691  * @param {Object} config The config object
20692  */
20693 Roo.bootstrap.Radio = function(config){
20694     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20695     
20696 };
20697
20698 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20699     
20700     boxLabel : '',
20701     
20702     value : '',
20703     
20704     getAutoCreate : function()
20705     {
20706         var cfg = {
20707             tag : 'div',
20708             cls : 'form-group radio',
20709             cn : [
20710                 {
20711                     tag : 'label',
20712                     cls : 'box-label',
20713                     html : this.boxLabel
20714                 }
20715             ]
20716         };
20717         
20718         return cfg;
20719     },
20720     
20721     initEvents : function() 
20722     {
20723         this.parent().register(this);
20724         
20725         this.el.on('click', this.onClick, this);
20726         
20727     },
20728     
20729     onClick : function()
20730     {
20731         this.setChecked(true);
20732     },
20733     
20734     setChecked : function(state, suppressEvent)
20735     {
20736         this.parent().setValue(this.value, suppressEvent);
20737         
20738     }
20739     
20740 });
20741  
20742
20743  /*
20744  * - LGPL
20745  *
20746  * Input
20747  * 
20748  */
20749
20750 /**
20751  * @class Roo.bootstrap.SecurePass
20752  * @extends Roo.bootstrap.Input
20753  * Bootstrap SecurePass class
20754  *
20755  * 
20756  * @constructor
20757  * Create a new SecurePass
20758  * @param {Object} config The config object
20759  */
20760  
20761 Roo.bootstrap.SecurePass = function (config) {
20762     // these go here, so the translation tool can replace them..
20763     this.errors = {
20764         PwdEmpty: "Please type a password, and then retype it to confirm.",
20765         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20766         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20767         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20768         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20769         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20770         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20771         TooWeak: "Your password is Too Weak."
20772     },
20773     this.meterLabel = "Password strength:";
20774     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20775     this.meterClass = [
20776         "roo-password-meter-tooweak", 
20777         "roo-password-meter-weak", 
20778         "roo-password-meter-medium", 
20779         "roo-password-meter-strong", 
20780         "roo-password-meter-grey"
20781     ];
20782     
20783     this.errors = {};
20784     
20785     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20786 }
20787
20788 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20789     /**
20790      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20791      * {
20792      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20793      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20794      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20795      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20796      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20797      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20798      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20799      * })
20800      */
20801     // private
20802     
20803     meterWidth: 300,
20804     errorMsg :'',    
20805     errors: false,
20806     imageRoot: '/',
20807     /**
20808      * @cfg {String/Object} Label for the strength meter (defaults to
20809      * 'Password strength:')
20810      */
20811     // private
20812     meterLabel: '',
20813     /**
20814      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20815      * ['Weak', 'Medium', 'Strong'])
20816      */
20817     // private    
20818     pwdStrengths: false,    
20819     // private
20820     strength: 0,
20821     // private
20822     _lastPwd: null,
20823     // private
20824     kCapitalLetter: 0,
20825     kSmallLetter: 1,
20826     kDigit: 2,
20827     kPunctuation: 3,
20828     
20829     insecure: false,
20830     // private
20831     initEvents: function ()
20832     {
20833         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20834
20835         if (this.el.is('input[type=password]') && Roo.isSafari) {
20836             this.el.on('keydown', this.SafariOnKeyDown, this);
20837         }
20838
20839         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20840     },
20841     // private
20842     onRender: function (ct, position)
20843     {
20844         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20845         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20846         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20847
20848         this.trigger.createChild({
20849                    cn: [
20850                     {
20851                     //id: 'PwdMeter',
20852                     tag: 'div',
20853                     cls: 'roo-password-meter-grey col-xs-12',
20854                     style: {
20855                         //width: 0,
20856                         //width: this.meterWidth + 'px'                                                
20857                         }
20858                     },
20859                     {                            
20860                          cls: 'roo-password-meter-text'                          
20861                     }
20862                 ]            
20863         });
20864
20865          
20866         if (this.hideTrigger) {
20867             this.trigger.setDisplayed(false);
20868         }
20869         this.setSize(this.width || '', this.height || '');
20870     },
20871     // private
20872     onDestroy: function ()
20873     {
20874         if (this.trigger) {
20875             this.trigger.removeAllListeners();
20876             this.trigger.remove();
20877         }
20878         if (this.wrap) {
20879             this.wrap.remove();
20880         }
20881         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20882     },
20883     // private
20884     checkStrength: function ()
20885     {
20886         var pwd = this.inputEl().getValue();
20887         if (pwd == this._lastPwd) {
20888             return;
20889         }
20890
20891         var strength;
20892         if (this.ClientSideStrongPassword(pwd)) {
20893             strength = 3;
20894         } else if (this.ClientSideMediumPassword(pwd)) {
20895             strength = 2;
20896         } else if (this.ClientSideWeakPassword(pwd)) {
20897             strength = 1;
20898         } else {
20899             strength = 0;
20900         }
20901         
20902         Roo.log('strength1: ' + strength);
20903         
20904         //var pm = this.trigger.child('div/div/div').dom;
20905         var pm = this.trigger.child('div/div');
20906         pm.removeClass(this.meterClass);
20907         pm.addClass(this.meterClass[strength]);
20908                 
20909         
20910         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20911                 
20912         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20913         
20914         this._lastPwd = pwd;
20915     },
20916     reset: function ()
20917     {
20918         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20919         
20920         this._lastPwd = '';
20921         
20922         var pm = this.trigger.child('div/div');
20923         pm.removeClass(this.meterClass);
20924         pm.addClass('roo-password-meter-grey');        
20925         
20926         
20927         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20928         
20929         pt.innerHTML = '';
20930         this.inputEl().dom.type='password';
20931     },
20932     // private
20933     validateValue: function (value)
20934     {
20935         
20936         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20937             return false;
20938         }
20939         if (value.length == 0) {
20940             if (this.allowBlank) {
20941                 this.clearInvalid();
20942                 return true;
20943             }
20944
20945             this.markInvalid(this.errors.PwdEmpty);
20946             this.errorMsg = this.errors.PwdEmpty;
20947             return false;
20948         }
20949         
20950         if(this.insecure){
20951             return true;
20952         }
20953         
20954         if ('[\x21-\x7e]*'.match(value)) {
20955             this.markInvalid(this.errors.PwdBadChar);
20956             this.errorMsg = this.errors.PwdBadChar;
20957             return false;
20958         }
20959         if (value.length < 6) {
20960             this.markInvalid(this.errors.PwdShort);
20961             this.errorMsg = this.errors.PwdShort;
20962             return false;
20963         }
20964         if (value.length > 16) {
20965             this.markInvalid(this.errors.PwdLong);
20966             this.errorMsg = this.errors.PwdLong;
20967             return false;
20968         }
20969         var strength;
20970         if (this.ClientSideStrongPassword(value)) {
20971             strength = 3;
20972         } else if (this.ClientSideMediumPassword(value)) {
20973             strength = 2;
20974         } else if (this.ClientSideWeakPassword(value)) {
20975             strength = 1;
20976         } else {
20977             strength = 0;
20978         }
20979
20980         
20981         if (strength < 2) {
20982             //this.markInvalid(this.errors.TooWeak);
20983             this.errorMsg = this.errors.TooWeak;
20984             //return false;
20985         }
20986         
20987         
20988         console.log('strength2: ' + strength);
20989         
20990         //var pm = this.trigger.child('div/div/div').dom;
20991         
20992         var pm = this.trigger.child('div/div');
20993         pm.removeClass(this.meterClass);
20994         pm.addClass(this.meterClass[strength]);
20995                 
20996         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20997                 
20998         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20999         
21000         this.errorMsg = ''; 
21001         return true;
21002     },
21003     // private
21004     CharacterSetChecks: function (type)
21005     {
21006         this.type = type;
21007         this.fResult = false;
21008     },
21009     // private
21010     isctype: function (character, type)
21011     {
21012         switch (type) {  
21013             case this.kCapitalLetter:
21014                 if (character >= 'A' && character <= 'Z') {
21015                     return true;
21016                 }
21017                 break;
21018             
21019             case this.kSmallLetter:
21020                 if (character >= 'a' && character <= 'z') {
21021                     return true;
21022                 }
21023                 break;
21024             
21025             case this.kDigit:
21026                 if (character >= '0' && character <= '9') {
21027                     return true;
21028                 }
21029                 break;
21030             
21031             case this.kPunctuation:
21032                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21033                     return true;
21034                 }
21035                 break;
21036             
21037             default:
21038                 return false;
21039         }
21040
21041     },
21042     // private
21043     IsLongEnough: function (pwd, size)
21044     {
21045         return !(pwd == null || isNaN(size) || pwd.length < size);
21046     },
21047     // private
21048     SpansEnoughCharacterSets: function (word, nb)
21049     {
21050         if (!this.IsLongEnough(word, nb))
21051         {
21052             return false;
21053         }
21054
21055         var characterSetChecks = new Array(
21056             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21057             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21058         );
21059         
21060         for (var index = 0; index < word.length; ++index) {
21061             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21062                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21063                     characterSetChecks[nCharSet].fResult = true;
21064                     break;
21065                 }
21066             }
21067         }
21068
21069         var nCharSets = 0;
21070         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21071             if (characterSetChecks[nCharSet].fResult) {
21072                 ++nCharSets;
21073             }
21074         }
21075
21076         if (nCharSets < nb) {
21077             return false;
21078         }
21079         return true;
21080     },
21081     // private
21082     ClientSideStrongPassword: function (pwd)
21083     {
21084         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21085     },
21086     // private
21087     ClientSideMediumPassword: function (pwd)
21088     {
21089         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21090     },
21091     // private
21092     ClientSideWeakPassword: function (pwd)
21093     {
21094         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21095     }
21096           
21097 })//<script type="text/javascript">
21098
21099 /*
21100  * Based  Ext JS Library 1.1.1
21101  * Copyright(c) 2006-2007, Ext JS, LLC.
21102  * LGPL
21103  *
21104  */
21105  
21106 /**
21107  * @class Roo.HtmlEditorCore
21108  * @extends Roo.Component
21109  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21110  *
21111  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21112  */
21113
21114 Roo.HtmlEditorCore = function(config){
21115     
21116     
21117     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21118     
21119     
21120     this.addEvents({
21121         /**
21122          * @event initialize
21123          * Fires when the editor is fully initialized (including the iframe)
21124          * @param {Roo.HtmlEditorCore} this
21125          */
21126         initialize: true,
21127         /**
21128          * @event activate
21129          * Fires when the editor is first receives the focus. Any insertion must wait
21130          * until after this event.
21131          * @param {Roo.HtmlEditorCore} this
21132          */
21133         activate: true,
21134          /**
21135          * @event beforesync
21136          * Fires before the textarea is updated with content from the editor iframe. Return false
21137          * to cancel the sync.
21138          * @param {Roo.HtmlEditorCore} this
21139          * @param {String} html
21140          */
21141         beforesync: true,
21142          /**
21143          * @event beforepush
21144          * Fires before the iframe editor is updated with content from the textarea. Return false
21145          * to cancel the push.
21146          * @param {Roo.HtmlEditorCore} this
21147          * @param {String} html
21148          */
21149         beforepush: true,
21150          /**
21151          * @event sync
21152          * Fires when the textarea is updated with content from the editor iframe.
21153          * @param {Roo.HtmlEditorCore} this
21154          * @param {String} html
21155          */
21156         sync: true,
21157          /**
21158          * @event push
21159          * Fires when the iframe editor is updated with content from the textarea.
21160          * @param {Roo.HtmlEditorCore} this
21161          * @param {String} html
21162          */
21163         push: true,
21164         
21165         /**
21166          * @event editorevent
21167          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21168          * @param {Roo.HtmlEditorCore} this
21169          */
21170         editorevent: true
21171         
21172     });
21173     
21174     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21175     
21176     // defaults : white / black...
21177     this.applyBlacklists();
21178     
21179     
21180     
21181 };
21182
21183
21184 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21185
21186
21187      /**
21188      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21189      */
21190     
21191     owner : false,
21192     
21193      /**
21194      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21195      *                        Roo.resizable.
21196      */
21197     resizable : false,
21198      /**
21199      * @cfg {Number} height (in pixels)
21200      */   
21201     height: 300,
21202    /**
21203      * @cfg {Number} width (in pixels)
21204      */   
21205     width: 500,
21206     
21207     /**
21208      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21209      * 
21210      */
21211     stylesheets: false,
21212     
21213     // id of frame..
21214     frameId: false,
21215     
21216     // private properties
21217     validationEvent : false,
21218     deferHeight: true,
21219     initialized : false,
21220     activated : false,
21221     sourceEditMode : false,
21222     onFocus : Roo.emptyFn,
21223     iframePad:3,
21224     hideMode:'offsets',
21225     
21226     clearUp: true,
21227     
21228     // blacklist + whitelisted elements..
21229     black: false,
21230     white: false,
21231      
21232     
21233
21234     /**
21235      * Protected method that will not generally be called directly. It
21236      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21237      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21238      */
21239     getDocMarkup : function(){
21240         // body styles..
21241         var st = '';
21242         
21243         // inherit styels from page...?? 
21244         if (this.stylesheets === false) {
21245             
21246             Roo.get(document.head).select('style').each(function(node) {
21247                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21248             });
21249             
21250             Roo.get(document.head).select('link').each(function(node) { 
21251                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21252             });
21253             
21254         } else if (!this.stylesheets.length) {
21255                 // simple..
21256                 st = '<style type="text/css">' +
21257                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21258                    '</style>';
21259         } else { 
21260             
21261         }
21262         
21263         st +=  '<style type="text/css">' +
21264             'IMG { cursor: pointer } ' +
21265         '</style>';
21266
21267         
21268         return '<html><head>' + st  +
21269             //<style type="text/css">' +
21270             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21271             //'</style>' +
21272             ' </head><body class="roo-htmleditor-body"></body></html>';
21273     },
21274
21275     // private
21276     onRender : function(ct, position)
21277     {
21278         var _t = this;
21279         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21280         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21281         
21282         
21283         this.el.dom.style.border = '0 none';
21284         this.el.dom.setAttribute('tabIndex', -1);
21285         this.el.addClass('x-hidden hide');
21286         
21287         
21288         
21289         if(Roo.isIE){ // fix IE 1px bogus margin
21290             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21291         }
21292        
21293         
21294         this.frameId = Roo.id();
21295         
21296          
21297         
21298         var iframe = this.owner.wrap.createChild({
21299             tag: 'iframe',
21300             cls: 'form-control', // bootstrap..
21301             id: this.frameId,
21302             name: this.frameId,
21303             frameBorder : 'no',
21304             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21305         }, this.el
21306         );
21307         
21308         
21309         this.iframe = iframe.dom;
21310
21311          this.assignDocWin();
21312         
21313         this.doc.designMode = 'on';
21314        
21315         this.doc.open();
21316         this.doc.write(this.getDocMarkup());
21317         this.doc.close();
21318
21319         
21320         var task = { // must defer to wait for browser to be ready
21321             run : function(){
21322                 //console.log("run task?" + this.doc.readyState);
21323                 this.assignDocWin();
21324                 if(this.doc.body || this.doc.readyState == 'complete'){
21325                     try {
21326                         this.doc.designMode="on";
21327                     } catch (e) {
21328                         return;
21329                     }
21330                     Roo.TaskMgr.stop(task);
21331                     this.initEditor.defer(10, this);
21332                 }
21333             },
21334             interval : 10,
21335             duration: 10000,
21336             scope: this
21337         };
21338         Roo.TaskMgr.start(task);
21339
21340     },
21341
21342     // private
21343     onResize : function(w, h)
21344     {
21345          Roo.log('resize: ' +w + ',' + h );
21346         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21347         if(!this.iframe){
21348             return;
21349         }
21350         if(typeof w == 'number'){
21351             
21352             this.iframe.style.width = w + 'px';
21353         }
21354         if(typeof h == 'number'){
21355             
21356             this.iframe.style.height = h + 'px';
21357             if(this.doc){
21358                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21359             }
21360         }
21361         
21362     },
21363
21364     /**
21365      * Toggles the editor between standard and source edit mode.
21366      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21367      */
21368     toggleSourceEdit : function(sourceEditMode){
21369         
21370         this.sourceEditMode = sourceEditMode === true;
21371         
21372         if(this.sourceEditMode){
21373  
21374             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21375             
21376         }else{
21377             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21378             //this.iframe.className = '';
21379             this.deferFocus();
21380         }
21381         //this.setSize(this.owner.wrap.getSize());
21382         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21383     },
21384
21385     
21386   
21387
21388     /**
21389      * Protected method that will not generally be called directly. If you need/want
21390      * custom HTML cleanup, this is the method you should override.
21391      * @param {String} html The HTML to be cleaned
21392      * return {String} The cleaned HTML
21393      */
21394     cleanHtml : function(html){
21395         html = String(html);
21396         if(html.length > 5){
21397             if(Roo.isSafari){ // strip safari nonsense
21398                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21399             }
21400         }
21401         if(html == '&nbsp;'){
21402             html = '';
21403         }
21404         return html;
21405     },
21406
21407     /**
21408      * HTML Editor -> Textarea
21409      * Protected method that will not generally be called directly. Syncs the contents
21410      * of the editor iframe with the textarea.
21411      */
21412     syncValue : function(){
21413         if(this.initialized){
21414             var bd = (this.doc.body || this.doc.documentElement);
21415             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21416             var html = bd.innerHTML;
21417             if(Roo.isSafari){
21418                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21419                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21420                 if(m && m[1]){
21421                     html = '<div style="'+m[0]+'">' + html + '</div>';
21422                 }
21423             }
21424             html = this.cleanHtml(html);
21425             // fix up the special chars.. normaly like back quotes in word...
21426             // however we do not want to do this with chinese..
21427             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21428                 var cc = b.charCodeAt();
21429                 if (
21430                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21431                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21432                     (cc >= 0xf900 && cc < 0xfb00 )
21433                 ) {
21434                         return b;
21435                 }
21436                 return "&#"+cc+";" 
21437             });
21438             if(this.owner.fireEvent('beforesync', this, html) !== false){
21439                 this.el.dom.value = html;
21440                 this.owner.fireEvent('sync', this, html);
21441             }
21442         }
21443     },
21444
21445     /**
21446      * Protected method that will not generally be called directly. Pushes the value of the textarea
21447      * into the iframe editor.
21448      */
21449     pushValue : function(){
21450         if(this.initialized){
21451             var v = this.el.dom.value.trim();
21452             
21453 //            if(v.length < 1){
21454 //                v = '&#160;';
21455 //            }
21456             
21457             if(this.owner.fireEvent('beforepush', this, v) !== false){
21458                 var d = (this.doc.body || this.doc.documentElement);
21459                 d.innerHTML = v;
21460                 this.cleanUpPaste();
21461                 this.el.dom.value = d.innerHTML;
21462                 this.owner.fireEvent('push', this, v);
21463             }
21464         }
21465     },
21466
21467     // private
21468     deferFocus : function(){
21469         this.focus.defer(10, this);
21470     },
21471
21472     // doc'ed in Field
21473     focus : function(){
21474         if(this.win && !this.sourceEditMode){
21475             this.win.focus();
21476         }else{
21477             this.el.focus();
21478         }
21479     },
21480     
21481     assignDocWin: function()
21482     {
21483         var iframe = this.iframe;
21484         
21485          if(Roo.isIE){
21486             this.doc = iframe.contentWindow.document;
21487             this.win = iframe.contentWindow;
21488         } else {
21489 //            if (!Roo.get(this.frameId)) {
21490 //                return;
21491 //            }
21492 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21493 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21494             
21495             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21496                 return;
21497             }
21498             
21499             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21500             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21501         }
21502     },
21503     
21504     // private
21505     initEditor : function(){
21506         //console.log("INIT EDITOR");
21507         this.assignDocWin();
21508         
21509         
21510         
21511         this.doc.designMode="on";
21512         this.doc.open();
21513         this.doc.write(this.getDocMarkup());
21514         this.doc.close();
21515         
21516         var dbody = (this.doc.body || this.doc.documentElement);
21517         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21518         // this copies styles from the containing element into thsi one..
21519         // not sure why we need all of this..
21520         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21521         
21522         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21523         //ss['background-attachment'] = 'fixed'; // w3c
21524         dbody.bgProperties = 'fixed'; // ie
21525         //Roo.DomHelper.applyStyles(dbody, ss);
21526         Roo.EventManager.on(this.doc, {
21527             //'mousedown': this.onEditorEvent,
21528             'mouseup': this.onEditorEvent,
21529             'dblclick': this.onEditorEvent,
21530             'click': this.onEditorEvent,
21531             'keyup': this.onEditorEvent,
21532             buffer:100,
21533             scope: this
21534         });
21535         if(Roo.isGecko){
21536             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21537         }
21538         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21539             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21540         }
21541         this.initialized = true;
21542
21543         this.owner.fireEvent('initialize', this);
21544         this.pushValue();
21545     },
21546
21547     // private
21548     onDestroy : function(){
21549         
21550         
21551         
21552         if(this.rendered){
21553             
21554             //for (var i =0; i < this.toolbars.length;i++) {
21555             //    // fixme - ask toolbars for heights?
21556             //    this.toolbars[i].onDestroy();
21557            // }
21558             
21559             //this.wrap.dom.innerHTML = '';
21560             //this.wrap.remove();
21561         }
21562     },
21563
21564     // private
21565     onFirstFocus : function(){
21566         
21567         this.assignDocWin();
21568         
21569         
21570         this.activated = true;
21571          
21572     
21573         if(Roo.isGecko){ // prevent silly gecko errors
21574             this.win.focus();
21575             var s = this.win.getSelection();
21576             if(!s.focusNode || s.focusNode.nodeType != 3){
21577                 var r = s.getRangeAt(0);
21578                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21579                 r.collapse(true);
21580                 this.deferFocus();
21581             }
21582             try{
21583                 this.execCmd('useCSS', true);
21584                 this.execCmd('styleWithCSS', false);
21585             }catch(e){}
21586         }
21587         this.owner.fireEvent('activate', this);
21588     },
21589
21590     // private
21591     adjustFont: function(btn){
21592         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21593         //if(Roo.isSafari){ // safari
21594         //    adjust *= 2;
21595        // }
21596         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21597         if(Roo.isSafari){ // safari
21598             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21599             v =  (v < 10) ? 10 : v;
21600             v =  (v > 48) ? 48 : v;
21601             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21602             
21603         }
21604         
21605         
21606         v = Math.max(1, v+adjust);
21607         
21608         this.execCmd('FontSize', v  );
21609     },
21610
21611     onEditorEvent : function(e)
21612     {
21613         this.owner.fireEvent('editorevent', this, e);
21614       //  this.updateToolbar();
21615         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21616     },
21617
21618     insertTag : function(tg)
21619     {
21620         // could be a bit smarter... -> wrap the current selected tRoo..
21621         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21622             
21623             range = this.createRange(this.getSelection());
21624             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21625             wrappingNode.appendChild(range.extractContents());
21626             range.insertNode(wrappingNode);
21627
21628             return;
21629             
21630             
21631             
21632         }
21633         this.execCmd("formatblock",   tg);
21634         
21635     },
21636     
21637     insertText : function(txt)
21638     {
21639         
21640         
21641         var range = this.createRange();
21642         range.deleteContents();
21643                //alert(Sender.getAttribute('label'));
21644                
21645         range.insertNode(this.doc.createTextNode(txt));
21646     } ,
21647     
21648      
21649
21650     /**
21651      * Executes a Midas editor command on the editor document and performs necessary focus and
21652      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21653      * @param {String} cmd The Midas command
21654      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21655      */
21656     relayCmd : function(cmd, value){
21657         this.win.focus();
21658         this.execCmd(cmd, value);
21659         this.owner.fireEvent('editorevent', this);
21660         //this.updateToolbar();
21661         this.owner.deferFocus();
21662     },
21663
21664     /**
21665      * Executes a Midas editor command directly on the editor document.
21666      * For visual commands, you should use {@link #relayCmd} instead.
21667      * <b>This should only be called after the editor is initialized.</b>
21668      * @param {String} cmd The Midas command
21669      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21670      */
21671     execCmd : function(cmd, value){
21672         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21673         this.syncValue();
21674     },
21675  
21676  
21677    
21678     /**
21679      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21680      * to insert tRoo.
21681      * @param {String} text | dom node.. 
21682      */
21683     insertAtCursor : function(text)
21684     {
21685         
21686         if(!this.activated){
21687             return;
21688         }
21689         /*
21690         if(Roo.isIE){
21691             this.win.focus();
21692             var r = this.doc.selection.createRange();
21693             if(r){
21694                 r.collapse(true);
21695                 r.pasteHTML(text);
21696                 this.syncValue();
21697                 this.deferFocus();
21698             
21699             }
21700             return;
21701         }
21702         */
21703         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21704             this.win.focus();
21705             
21706             
21707             // from jquery ui (MIT licenced)
21708             var range, node;
21709             var win = this.win;
21710             
21711             if (win.getSelection && win.getSelection().getRangeAt) {
21712                 range = win.getSelection().getRangeAt(0);
21713                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21714                 range.insertNode(node);
21715             } else if (win.document.selection && win.document.selection.createRange) {
21716                 // no firefox support
21717                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21718                 win.document.selection.createRange().pasteHTML(txt);
21719             } else {
21720                 // no firefox support
21721                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21722                 this.execCmd('InsertHTML', txt);
21723             } 
21724             
21725             this.syncValue();
21726             
21727             this.deferFocus();
21728         }
21729     },
21730  // private
21731     mozKeyPress : function(e){
21732         if(e.ctrlKey){
21733             var c = e.getCharCode(), cmd;
21734           
21735             if(c > 0){
21736                 c = String.fromCharCode(c).toLowerCase();
21737                 switch(c){
21738                     case 'b':
21739                         cmd = 'bold';
21740                         break;
21741                     case 'i':
21742                         cmd = 'italic';
21743                         break;
21744                     
21745                     case 'u':
21746                         cmd = 'underline';
21747                         break;
21748                     
21749                     case 'v':
21750                         this.cleanUpPaste.defer(100, this);
21751                         return;
21752                         
21753                 }
21754                 if(cmd){
21755                     this.win.focus();
21756                     this.execCmd(cmd);
21757                     this.deferFocus();
21758                     e.preventDefault();
21759                 }
21760                 
21761             }
21762         }
21763     },
21764
21765     // private
21766     fixKeys : function(){ // load time branching for fastest keydown performance
21767         if(Roo.isIE){
21768             return function(e){
21769                 var k = e.getKey(), r;
21770                 if(k == e.TAB){
21771                     e.stopEvent();
21772                     r = this.doc.selection.createRange();
21773                     if(r){
21774                         r.collapse(true);
21775                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21776                         this.deferFocus();
21777                     }
21778                     return;
21779                 }
21780                 
21781                 if(k == e.ENTER){
21782                     r = this.doc.selection.createRange();
21783                     if(r){
21784                         var target = r.parentElement();
21785                         if(!target || target.tagName.toLowerCase() != 'li'){
21786                             e.stopEvent();
21787                             r.pasteHTML('<br />');
21788                             r.collapse(false);
21789                             r.select();
21790                         }
21791                     }
21792                 }
21793                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21794                     this.cleanUpPaste.defer(100, this);
21795                     return;
21796                 }
21797                 
21798                 
21799             };
21800         }else if(Roo.isOpera){
21801             return function(e){
21802                 var k = e.getKey();
21803                 if(k == e.TAB){
21804                     e.stopEvent();
21805                     this.win.focus();
21806                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21807                     this.deferFocus();
21808                 }
21809                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21810                     this.cleanUpPaste.defer(100, this);
21811                     return;
21812                 }
21813                 
21814             };
21815         }else if(Roo.isSafari){
21816             return function(e){
21817                 var k = e.getKey();
21818                 
21819                 if(k == e.TAB){
21820                     e.stopEvent();
21821                     this.execCmd('InsertText','\t');
21822                     this.deferFocus();
21823                     return;
21824                 }
21825                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21826                     this.cleanUpPaste.defer(100, this);
21827                     return;
21828                 }
21829                 
21830              };
21831         }
21832     }(),
21833     
21834     getAllAncestors: function()
21835     {
21836         var p = this.getSelectedNode();
21837         var a = [];
21838         if (!p) {
21839             a.push(p); // push blank onto stack..
21840             p = this.getParentElement();
21841         }
21842         
21843         
21844         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21845             a.push(p);
21846             p = p.parentNode;
21847         }
21848         a.push(this.doc.body);
21849         return a;
21850     },
21851     lastSel : false,
21852     lastSelNode : false,
21853     
21854     
21855     getSelection : function() 
21856     {
21857         this.assignDocWin();
21858         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21859     },
21860     
21861     getSelectedNode: function() 
21862     {
21863         // this may only work on Gecko!!!
21864         
21865         // should we cache this!!!!
21866         
21867         
21868         
21869          
21870         var range = this.createRange(this.getSelection()).cloneRange();
21871         
21872         if (Roo.isIE) {
21873             var parent = range.parentElement();
21874             while (true) {
21875                 var testRange = range.duplicate();
21876                 testRange.moveToElementText(parent);
21877                 if (testRange.inRange(range)) {
21878                     break;
21879                 }
21880                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21881                     break;
21882                 }
21883                 parent = parent.parentElement;
21884             }
21885             return parent;
21886         }
21887         
21888         // is ancestor a text element.
21889         var ac =  range.commonAncestorContainer;
21890         if (ac.nodeType == 3) {
21891             ac = ac.parentNode;
21892         }
21893         
21894         var ar = ac.childNodes;
21895          
21896         var nodes = [];
21897         var other_nodes = [];
21898         var has_other_nodes = false;
21899         for (var i=0;i<ar.length;i++) {
21900             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21901                 continue;
21902             }
21903             // fullly contained node.
21904             
21905             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21906                 nodes.push(ar[i]);
21907                 continue;
21908             }
21909             
21910             // probably selected..
21911             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21912                 other_nodes.push(ar[i]);
21913                 continue;
21914             }
21915             // outer..
21916             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21917                 continue;
21918             }
21919             
21920             
21921             has_other_nodes = true;
21922         }
21923         if (!nodes.length && other_nodes.length) {
21924             nodes= other_nodes;
21925         }
21926         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21927             return false;
21928         }
21929         
21930         return nodes[0];
21931     },
21932     createRange: function(sel)
21933     {
21934         // this has strange effects when using with 
21935         // top toolbar - not sure if it's a great idea.
21936         //this.editor.contentWindow.focus();
21937         if (typeof sel != "undefined") {
21938             try {
21939                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21940             } catch(e) {
21941                 return this.doc.createRange();
21942             }
21943         } else {
21944             return this.doc.createRange();
21945         }
21946     },
21947     getParentElement: function()
21948     {
21949         
21950         this.assignDocWin();
21951         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21952         
21953         var range = this.createRange(sel);
21954          
21955         try {
21956             var p = range.commonAncestorContainer;
21957             while (p.nodeType == 3) { // text node
21958                 p = p.parentNode;
21959             }
21960             return p;
21961         } catch (e) {
21962             return null;
21963         }
21964     
21965     },
21966     /***
21967      *
21968      * Range intersection.. the hard stuff...
21969      *  '-1' = before
21970      *  '0' = hits..
21971      *  '1' = after.
21972      *         [ -- selected range --- ]
21973      *   [fail]                        [fail]
21974      *
21975      *    basically..
21976      *      if end is before start or  hits it. fail.
21977      *      if start is after end or hits it fail.
21978      *
21979      *   if either hits (but other is outside. - then it's not 
21980      *   
21981      *    
21982      **/
21983     
21984     
21985     // @see http://www.thismuchiknow.co.uk/?p=64.
21986     rangeIntersectsNode : function(range, node)
21987     {
21988         var nodeRange = node.ownerDocument.createRange();
21989         try {
21990             nodeRange.selectNode(node);
21991         } catch (e) {
21992             nodeRange.selectNodeContents(node);
21993         }
21994     
21995         var rangeStartRange = range.cloneRange();
21996         rangeStartRange.collapse(true);
21997     
21998         var rangeEndRange = range.cloneRange();
21999         rangeEndRange.collapse(false);
22000     
22001         var nodeStartRange = nodeRange.cloneRange();
22002         nodeStartRange.collapse(true);
22003     
22004         var nodeEndRange = nodeRange.cloneRange();
22005         nodeEndRange.collapse(false);
22006     
22007         return rangeStartRange.compareBoundaryPoints(
22008                  Range.START_TO_START, nodeEndRange) == -1 &&
22009                rangeEndRange.compareBoundaryPoints(
22010                  Range.START_TO_START, nodeStartRange) == 1;
22011         
22012          
22013     },
22014     rangeCompareNode : function(range, node)
22015     {
22016         var nodeRange = node.ownerDocument.createRange();
22017         try {
22018             nodeRange.selectNode(node);
22019         } catch (e) {
22020             nodeRange.selectNodeContents(node);
22021         }
22022         
22023         
22024         range.collapse(true);
22025     
22026         nodeRange.collapse(true);
22027      
22028         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22029         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22030          
22031         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22032         
22033         var nodeIsBefore   =  ss == 1;
22034         var nodeIsAfter    = ee == -1;
22035         
22036         if (nodeIsBefore && nodeIsAfter) {
22037             return 0; // outer
22038         }
22039         if (!nodeIsBefore && nodeIsAfter) {
22040             return 1; //right trailed.
22041         }
22042         
22043         if (nodeIsBefore && !nodeIsAfter) {
22044             return 2;  // left trailed.
22045         }
22046         // fully contined.
22047         return 3;
22048     },
22049
22050     // private? - in a new class?
22051     cleanUpPaste :  function()
22052     {
22053         // cleans up the whole document..
22054         Roo.log('cleanuppaste');
22055         
22056         this.cleanUpChildren(this.doc.body);
22057         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22058         if (clean != this.doc.body.innerHTML) {
22059             this.doc.body.innerHTML = clean;
22060         }
22061         
22062     },
22063     
22064     cleanWordChars : function(input) {// change the chars to hex code
22065         var he = Roo.HtmlEditorCore;
22066         
22067         var output = input;
22068         Roo.each(he.swapCodes, function(sw) { 
22069             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22070             
22071             output = output.replace(swapper, sw[1]);
22072         });
22073         
22074         return output;
22075     },
22076     
22077     
22078     cleanUpChildren : function (n)
22079     {
22080         if (!n.childNodes.length) {
22081             return;
22082         }
22083         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22084            this.cleanUpChild(n.childNodes[i]);
22085         }
22086     },
22087     
22088     
22089         
22090     
22091     cleanUpChild : function (node)
22092     {
22093         var ed = this;
22094         //console.log(node);
22095         if (node.nodeName == "#text") {
22096             // clean up silly Windows -- stuff?
22097             return; 
22098         }
22099         if (node.nodeName == "#comment") {
22100             node.parentNode.removeChild(node);
22101             // clean up silly Windows -- stuff?
22102             return; 
22103         }
22104         var lcname = node.tagName.toLowerCase();
22105         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22106         // whitelist of tags..
22107         
22108         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22109             // remove node.
22110             node.parentNode.removeChild(node);
22111             return;
22112             
22113         }
22114         
22115         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22116         
22117         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22118         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22119         
22120         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22121         //    remove_keep_children = true;
22122         //}
22123         
22124         if (remove_keep_children) {
22125             this.cleanUpChildren(node);
22126             // inserts everything just before this node...
22127             while (node.childNodes.length) {
22128                 var cn = node.childNodes[0];
22129                 node.removeChild(cn);
22130                 node.parentNode.insertBefore(cn, node);
22131             }
22132             node.parentNode.removeChild(node);
22133             return;
22134         }
22135         
22136         if (!node.attributes || !node.attributes.length) {
22137             this.cleanUpChildren(node);
22138             return;
22139         }
22140         
22141         function cleanAttr(n,v)
22142         {
22143             
22144             if (v.match(/^\./) || v.match(/^\//)) {
22145                 return;
22146             }
22147             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22148                 return;
22149             }
22150             if (v.match(/^#/)) {
22151                 return;
22152             }
22153 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22154             node.removeAttribute(n);
22155             
22156         }
22157         
22158         var cwhite = this.cwhite;
22159         var cblack = this.cblack;
22160             
22161         function cleanStyle(n,v)
22162         {
22163             if (v.match(/expression/)) { //XSS?? should we even bother..
22164                 node.removeAttribute(n);
22165                 return;
22166             }
22167             
22168             var parts = v.split(/;/);
22169             var clean = [];
22170             
22171             Roo.each(parts, function(p) {
22172                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22173                 if (!p.length) {
22174                     return true;
22175                 }
22176                 var l = p.split(':').shift().replace(/\s+/g,'');
22177                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22178                 
22179                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22181                     //node.removeAttribute(n);
22182                     return true;
22183                 }
22184                 //Roo.log()
22185                 // only allow 'c whitelisted system attributes'
22186                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22187 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22188                     //node.removeAttribute(n);
22189                     return true;
22190                 }
22191                 
22192                 
22193                  
22194                 
22195                 clean.push(p);
22196                 return true;
22197             });
22198             if (clean.length) { 
22199                 node.setAttribute(n, clean.join(';'));
22200             } else {
22201                 node.removeAttribute(n);
22202             }
22203             
22204         }
22205         
22206         
22207         for (var i = node.attributes.length-1; i > -1 ; i--) {
22208             var a = node.attributes[i];
22209             //console.log(a);
22210             
22211             if (a.name.toLowerCase().substr(0,2)=='on')  {
22212                 node.removeAttribute(a.name);
22213                 continue;
22214             }
22215             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22216                 node.removeAttribute(a.name);
22217                 continue;
22218             }
22219             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22220                 cleanAttr(a.name,a.value); // fixme..
22221                 continue;
22222             }
22223             if (a.name == 'style') {
22224                 cleanStyle(a.name,a.value);
22225                 continue;
22226             }
22227             /// clean up MS crap..
22228             // tecnically this should be a list of valid class'es..
22229             
22230             
22231             if (a.name == 'class') {
22232                 if (a.value.match(/^Mso/)) {
22233                     node.className = '';
22234                 }
22235                 
22236                 if (a.value.match(/^body$/)) {
22237                     node.className = '';
22238                 }
22239                 continue;
22240             }
22241             
22242             // style cleanup!?
22243             // class cleanup?
22244             
22245         }
22246         
22247         
22248         this.cleanUpChildren(node);
22249         
22250         
22251     },
22252     
22253     /**
22254      * Clean up MS wordisms...
22255      */
22256     cleanWord : function(node)
22257     {
22258         
22259         
22260         if (!node) {
22261             this.cleanWord(this.doc.body);
22262             return;
22263         }
22264         if (node.nodeName == "#text") {
22265             // clean up silly Windows -- stuff?
22266             return; 
22267         }
22268         if (node.nodeName == "#comment") {
22269             node.parentNode.removeChild(node);
22270             // clean up silly Windows -- stuff?
22271             return; 
22272         }
22273         
22274         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22275             node.parentNode.removeChild(node);
22276             return;
22277         }
22278         
22279         // remove - but keep children..
22280         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22281             while (node.childNodes.length) {
22282                 var cn = node.childNodes[0];
22283                 node.removeChild(cn);
22284                 node.parentNode.insertBefore(cn, node);
22285             }
22286             node.parentNode.removeChild(node);
22287             this.iterateChildren(node, this.cleanWord);
22288             return;
22289         }
22290         // clean styles
22291         if (node.className.length) {
22292             
22293             var cn = node.className.split(/\W+/);
22294             var cna = [];
22295             Roo.each(cn, function(cls) {
22296                 if (cls.match(/Mso[a-zA-Z]+/)) {
22297                     return;
22298                 }
22299                 cna.push(cls);
22300             });
22301             node.className = cna.length ? cna.join(' ') : '';
22302             if (!cna.length) {
22303                 node.removeAttribute("class");
22304             }
22305         }
22306         
22307         if (node.hasAttribute("lang")) {
22308             node.removeAttribute("lang");
22309         }
22310         
22311         if (node.hasAttribute("style")) {
22312             
22313             var styles = node.getAttribute("style").split(";");
22314             var nstyle = [];
22315             Roo.each(styles, function(s) {
22316                 if (!s.match(/:/)) {
22317                     return;
22318                 }
22319                 var kv = s.split(":");
22320                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22321                     return;
22322                 }
22323                 // what ever is left... we allow.
22324                 nstyle.push(s);
22325             });
22326             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22327             if (!nstyle.length) {
22328                 node.removeAttribute('style');
22329             }
22330         }
22331         this.iterateChildren(node, this.cleanWord);
22332         
22333         
22334         
22335     },
22336     /**
22337      * iterateChildren of a Node, calling fn each time, using this as the scole..
22338      * @param {DomNode} node node to iterate children of.
22339      * @param {Function} fn method of this class to call on each item.
22340      */
22341     iterateChildren : function(node, fn)
22342     {
22343         if (!node.childNodes.length) {
22344                 return;
22345         }
22346         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22347            fn.call(this, node.childNodes[i])
22348         }
22349     },
22350     
22351     
22352     /**
22353      * cleanTableWidths.
22354      *
22355      * Quite often pasting from word etc.. results in tables with column and widths.
22356      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22357      *
22358      */
22359     cleanTableWidths : function(node)
22360     {
22361          
22362          
22363         if (!node) {
22364             this.cleanTableWidths(this.doc.body);
22365             return;
22366         }
22367         
22368         // ignore list...
22369         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22370             return; 
22371         }
22372         Roo.log(node.tagName);
22373         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22374             this.iterateChildren(node, this.cleanTableWidths);
22375             return;
22376         }
22377         if (node.hasAttribute('width')) {
22378             node.removeAttribute('width');
22379         }
22380         
22381          
22382         if (node.hasAttribute("style")) {
22383             // pretty basic...
22384             
22385             var styles = node.getAttribute("style").split(";");
22386             var nstyle = [];
22387             Roo.each(styles, function(s) {
22388                 if (!s.match(/:/)) {
22389                     return;
22390                 }
22391                 var kv = s.split(":");
22392                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22393                     return;
22394                 }
22395                 // what ever is left... we allow.
22396                 nstyle.push(s);
22397             });
22398             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22399             if (!nstyle.length) {
22400                 node.removeAttribute('style');
22401             }
22402         }
22403         
22404         this.iterateChildren(node, this.cleanTableWidths);
22405         
22406         
22407     },
22408     
22409     
22410     
22411     
22412     domToHTML : function(currentElement, depth, nopadtext) {
22413         
22414         depth = depth || 0;
22415         nopadtext = nopadtext || false;
22416     
22417         if (!currentElement) {
22418             return this.domToHTML(this.doc.body);
22419         }
22420         
22421         //Roo.log(currentElement);
22422         var j;
22423         var allText = false;
22424         var nodeName = currentElement.nodeName;
22425         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22426         
22427         if  (nodeName == '#text') {
22428             
22429             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22430         }
22431         
22432         
22433         var ret = '';
22434         if (nodeName != 'BODY') {
22435              
22436             var i = 0;
22437             // Prints the node tagName, such as <A>, <IMG>, etc
22438             if (tagName) {
22439                 var attr = [];
22440                 for(i = 0; i < currentElement.attributes.length;i++) {
22441                     // quoting?
22442                     var aname = currentElement.attributes.item(i).name;
22443                     if (!currentElement.attributes.item(i).value.length) {
22444                         continue;
22445                     }
22446                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22447                 }
22448                 
22449                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22450             } 
22451             else {
22452                 
22453                 // eack
22454             }
22455         } else {
22456             tagName = false;
22457         }
22458         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22459             return ret;
22460         }
22461         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22462             nopadtext = true;
22463         }
22464         
22465         
22466         // Traverse the tree
22467         i = 0;
22468         var currentElementChild = currentElement.childNodes.item(i);
22469         var allText = true;
22470         var innerHTML  = '';
22471         lastnode = '';
22472         while (currentElementChild) {
22473             // Formatting code (indent the tree so it looks nice on the screen)
22474             var nopad = nopadtext;
22475             if (lastnode == 'SPAN') {
22476                 nopad  = true;
22477             }
22478             // text
22479             if  (currentElementChild.nodeName == '#text') {
22480                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22481                 toadd = nopadtext ? toadd : toadd.trim();
22482                 if (!nopad && toadd.length > 80) {
22483                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22484                 }
22485                 innerHTML  += toadd;
22486                 
22487                 i++;
22488                 currentElementChild = currentElement.childNodes.item(i);
22489                 lastNode = '';
22490                 continue;
22491             }
22492             allText = false;
22493             
22494             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22495                 
22496             // Recursively traverse the tree structure of the child node
22497             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22498             lastnode = currentElementChild.nodeName;
22499             i++;
22500             currentElementChild=currentElement.childNodes.item(i);
22501         }
22502         
22503         ret += innerHTML;
22504         
22505         if (!allText) {
22506                 // The remaining code is mostly for formatting the tree
22507             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22508         }
22509         
22510         
22511         if (tagName) {
22512             ret+= "</"+tagName+">";
22513         }
22514         return ret;
22515         
22516     },
22517         
22518     applyBlacklists : function()
22519     {
22520         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22521         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22522         
22523         this.white = [];
22524         this.black = [];
22525         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22526             if (b.indexOf(tag) > -1) {
22527                 return;
22528             }
22529             this.white.push(tag);
22530             
22531         }, this);
22532         
22533         Roo.each(w, function(tag) {
22534             if (b.indexOf(tag) > -1) {
22535                 return;
22536             }
22537             if (this.white.indexOf(tag) > -1) {
22538                 return;
22539             }
22540             this.white.push(tag);
22541             
22542         }, this);
22543         
22544         
22545         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22546             if (w.indexOf(tag) > -1) {
22547                 return;
22548             }
22549             this.black.push(tag);
22550             
22551         }, this);
22552         
22553         Roo.each(b, function(tag) {
22554             if (w.indexOf(tag) > -1) {
22555                 return;
22556             }
22557             if (this.black.indexOf(tag) > -1) {
22558                 return;
22559             }
22560             this.black.push(tag);
22561             
22562         }, this);
22563         
22564         
22565         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22566         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22567         
22568         this.cwhite = [];
22569         this.cblack = [];
22570         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22571             if (b.indexOf(tag) > -1) {
22572                 return;
22573             }
22574             this.cwhite.push(tag);
22575             
22576         }, this);
22577         
22578         Roo.each(w, function(tag) {
22579             if (b.indexOf(tag) > -1) {
22580                 return;
22581             }
22582             if (this.cwhite.indexOf(tag) > -1) {
22583                 return;
22584             }
22585             this.cwhite.push(tag);
22586             
22587         }, this);
22588         
22589         
22590         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22591             if (w.indexOf(tag) > -1) {
22592                 return;
22593             }
22594             this.cblack.push(tag);
22595             
22596         }, this);
22597         
22598         Roo.each(b, function(tag) {
22599             if (w.indexOf(tag) > -1) {
22600                 return;
22601             }
22602             if (this.cblack.indexOf(tag) > -1) {
22603                 return;
22604             }
22605             this.cblack.push(tag);
22606             
22607         }, this);
22608     },
22609     
22610     setStylesheets : function(stylesheets)
22611     {
22612         if(typeof(stylesheets) == 'string'){
22613             Roo.get(this.iframe.contentDocument.head).createChild({
22614                 tag : 'link',
22615                 rel : 'stylesheet',
22616                 type : 'text/css',
22617                 href : stylesheets
22618             });
22619             
22620             return;
22621         }
22622         var _this = this;
22623      
22624         Roo.each(stylesheets, function(s) {
22625             if(!s.length){
22626                 return;
22627             }
22628             
22629             Roo.get(_this.iframe.contentDocument.head).createChild({
22630                 tag : 'link',
22631                 rel : 'stylesheet',
22632                 type : 'text/css',
22633                 href : s
22634             });
22635         });
22636
22637         
22638     },
22639     
22640     removeStylesheets : function()
22641     {
22642         var _this = this;
22643         
22644         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22645             s.remove();
22646         });
22647     }
22648     
22649     // hide stuff that is not compatible
22650     /**
22651      * @event blur
22652      * @hide
22653      */
22654     /**
22655      * @event change
22656      * @hide
22657      */
22658     /**
22659      * @event focus
22660      * @hide
22661      */
22662     /**
22663      * @event specialkey
22664      * @hide
22665      */
22666     /**
22667      * @cfg {String} fieldClass @hide
22668      */
22669     /**
22670      * @cfg {String} focusClass @hide
22671      */
22672     /**
22673      * @cfg {String} autoCreate @hide
22674      */
22675     /**
22676      * @cfg {String} inputType @hide
22677      */
22678     /**
22679      * @cfg {String} invalidClass @hide
22680      */
22681     /**
22682      * @cfg {String} invalidText @hide
22683      */
22684     /**
22685      * @cfg {String} msgFx @hide
22686      */
22687     /**
22688      * @cfg {String} validateOnBlur @hide
22689      */
22690 });
22691
22692 Roo.HtmlEditorCore.white = [
22693         'area', 'br', 'img', 'input', 'hr', 'wbr',
22694         
22695        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22696        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22697        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22698        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22699        'table',   'ul',         'xmp', 
22700        
22701        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22702       'thead',   'tr', 
22703      
22704       'dir', 'menu', 'ol', 'ul', 'dl',
22705        
22706       'embed',  'object'
22707 ];
22708
22709
22710 Roo.HtmlEditorCore.black = [
22711     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22712         'applet', // 
22713         'base',   'basefont', 'bgsound', 'blink',  'body', 
22714         'frame',  'frameset', 'head',    'html',   'ilayer', 
22715         'iframe', 'layer',  'link',     'meta',    'object',   
22716         'script', 'style' ,'title',  'xml' // clean later..
22717 ];
22718 Roo.HtmlEditorCore.clean = [
22719     'script', 'style', 'title', 'xml'
22720 ];
22721 Roo.HtmlEditorCore.remove = [
22722     'font'
22723 ];
22724 // attributes..
22725
22726 Roo.HtmlEditorCore.ablack = [
22727     'on'
22728 ];
22729     
22730 Roo.HtmlEditorCore.aclean = [ 
22731     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22732 ];
22733
22734 // protocols..
22735 Roo.HtmlEditorCore.pwhite= [
22736         'http',  'https',  'mailto'
22737 ];
22738
22739 // white listed style attributes.
22740 Roo.HtmlEditorCore.cwhite= [
22741       //  'text-align', /// default is to allow most things..
22742       
22743          
22744 //        'font-size'//??
22745 ];
22746
22747 // black listed style attributes.
22748 Roo.HtmlEditorCore.cblack= [
22749       //  'font-size' -- this can be set by the project 
22750 ];
22751
22752
22753 Roo.HtmlEditorCore.swapCodes   =[ 
22754     [    8211, "--" ], 
22755     [    8212, "--" ], 
22756     [    8216,  "'" ],  
22757     [    8217, "'" ],  
22758     [    8220, '"' ],  
22759     [    8221, '"' ],  
22760     [    8226, "*" ],  
22761     [    8230, "..." ]
22762 ]; 
22763
22764     /*
22765  * - LGPL
22766  *
22767  * HtmlEditor
22768  * 
22769  */
22770
22771 /**
22772  * @class Roo.bootstrap.HtmlEditor
22773  * @extends Roo.bootstrap.TextArea
22774  * Bootstrap HtmlEditor class
22775
22776  * @constructor
22777  * Create a new HtmlEditor
22778  * @param {Object} config The config object
22779  */
22780
22781 Roo.bootstrap.HtmlEditor = function(config){
22782     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22783     if (!this.toolbars) {
22784         this.toolbars = [];
22785     }
22786     
22787     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22788     this.addEvents({
22789             /**
22790              * @event initialize
22791              * Fires when the editor is fully initialized (including the iframe)
22792              * @param {HtmlEditor} this
22793              */
22794             initialize: true,
22795             /**
22796              * @event activate
22797              * Fires when the editor is first receives the focus. Any insertion must wait
22798              * until after this event.
22799              * @param {HtmlEditor} this
22800              */
22801             activate: true,
22802              /**
22803              * @event beforesync
22804              * Fires before the textarea is updated with content from the editor iframe. Return false
22805              * to cancel the sync.
22806              * @param {HtmlEditor} this
22807              * @param {String} html
22808              */
22809             beforesync: true,
22810              /**
22811              * @event beforepush
22812              * Fires before the iframe editor is updated with content from the textarea. Return false
22813              * to cancel the push.
22814              * @param {HtmlEditor} this
22815              * @param {String} html
22816              */
22817             beforepush: true,
22818              /**
22819              * @event sync
22820              * Fires when the textarea is updated with content from the editor iframe.
22821              * @param {HtmlEditor} this
22822              * @param {String} html
22823              */
22824             sync: true,
22825              /**
22826              * @event push
22827              * Fires when the iframe editor is updated with content from the textarea.
22828              * @param {HtmlEditor} this
22829              * @param {String} html
22830              */
22831             push: true,
22832              /**
22833              * @event editmodechange
22834              * Fires when the editor switches edit modes
22835              * @param {HtmlEditor} this
22836              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22837              */
22838             editmodechange: true,
22839             /**
22840              * @event editorevent
22841              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22842              * @param {HtmlEditor} this
22843              */
22844             editorevent: true,
22845             /**
22846              * @event firstfocus
22847              * Fires when on first focus - needed by toolbars..
22848              * @param {HtmlEditor} this
22849              */
22850             firstfocus: true,
22851             /**
22852              * @event autosave
22853              * Auto save the htmlEditor value as a file into Events
22854              * @param {HtmlEditor} this
22855              */
22856             autosave: true,
22857             /**
22858              * @event savedpreview
22859              * preview the saved version of htmlEditor
22860              * @param {HtmlEditor} this
22861              */
22862             savedpreview: true
22863         });
22864 };
22865
22866
22867 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22868     
22869     
22870       /**
22871      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22872      */
22873     toolbars : false,
22874     
22875      /**
22876     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22877     */
22878     btns : [],
22879    
22880      /**
22881      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22882      *                        Roo.resizable.
22883      */
22884     resizable : false,
22885      /**
22886      * @cfg {Number} height (in pixels)
22887      */   
22888     height: 300,
22889    /**
22890      * @cfg {Number} width (in pixels)
22891      */   
22892     width: false,
22893     
22894     /**
22895      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22896      * 
22897      */
22898     stylesheets: false,
22899     
22900     // id of frame..
22901     frameId: false,
22902     
22903     // private properties
22904     validationEvent : false,
22905     deferHeight: true,
22906     initialized : false,
22907     activated : false,
22908     
22909     onFocus : Roo.emptyFn,
22910     iframePad:3,
22911     hideMode:'offsets',
22912     
22913     tbContainer : false,
22914     
22915     toolbarContainer :function() {
22916         return this.wrap.select('.x-html-editor-tb',true).first();
22917     },
22918
22919     /**
22920      * Protected method that will not generally be called directly. It
22921      * is called when the editor creates its toolbar. Override this method if you need to
22922      * add custom toolbar buttons.
22923      * @param {HtmlEditor} editor
22924      */
22925     createToolbar : function(){
22926         Roo.log('renewing');
22927         Roo.log("create toolbars");
22928         
22929         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22930         this.toolbars[0].render(this.toolbarContainer());
22931         
22932         return;
22933         
22934 //        if (!editor.toolbars || !editor.toolbars.length) {
22935 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22936 //        }
22937 //        
22938 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22939 //            editor.toolbars[i] = Roo.factory(
22940 //                    typeof(editor.toolbars[i]) == 'string' ?
22941 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22942 //                Roo.bootstrap.HtmlEditor);
22943 //            editor.toolbars[i].init(editor);
22944 //        }
22945     },
22946
22947      
22948     // private
22949     onRender : function(ct, position)
22950     {
22951        // Roo.log("Call onRender: " + this.xtype);
22952         var _t = this;
22953         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22954       
22955         this.wrap = this.inputEl().wrap({
22956             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22957         });
22958         
22959         this.editorcore.onRender(ct, position);
22960          
22961         if (this.resizable) {
22962             this.resizeEl = new Roo.Resizable(this.wrap, {
22963                 pinned : true,
22964                 wrap: true,
22965                 dynamic : true,
22966                 minHeight : this.height,
22967                 height: this.height,
22968                 handles : this.resizable,
22969                 width: this.width,
22970                 listeners : {
22971                     resize : function(r, w, h) {
22972                         _t.onResize(w,h); // -something
22973                     }
22974                 }
22975             });
22976             
22977         }
22978         this.createToolbar(this);
22979        
22980         
22981         if(!this.width && this.resizable){
22982             this.setSize(this.wrap.getSize());
22983         }
22984         if (this.resizeEl) {
22985             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22986             // should trigger onReize..
22987         }
22988         
22989     },
22990
22991     // private
22992     onResize : function(w, h)
22993     {
22994         Roo.log('resize: ' +w + ',' + h );
22995         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22996         var ew = false;
22997         var eh = false;
22998         
22999         if(this.inputEl() ){
23000             if(typeof w == 'number'){
23001                 var aw = w - this.wrap.getFrameWidth('lr');
23002                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23003                 ew = aw;
23004             }
23005             if(typeof h == 'number'){
23006                  var tbh = -11;  // fixme it needs to tool bar size!
23007                 for (var i =0; i < this.toolbars.length;i++) {
23008                     // fixme - ask toolbars for heights?
23009                     tbh += this.toolbars[i].el.getHeight();
23010                     //if (this.toolbars[i].footer) {
23011                     //    tbh += this.toolbars[i].footer.el.getHeight();
23012                     //}
23013                 }
23014               
23015                 
23016                 
23017                 
23018                 
23019                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23020                 ah -= 5; // knock a few pixes off for look..
23021                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23022                 var eh = ah;
23023             }
23024         }
23025         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23026         this.editorcore.onResize(ew,eh);
23027         
23028     },
23029
23030     /**
23031      * Toggles the editor between standard and source edit mode.
23032      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23033      */
23034     toggleSourceEdit : function(sourceEditMode)
23035     {
23036         this.editorcore.toggleSourceEdit(sourceEditMode);
23037         
23038         if(this.editorcore.sourceEditMode){
23039             Roo.log('editor - showing textarea');
23040             
23041 //            Roo.log('in');
23042 //            Roo.log(this.syncValue());
23043             this.syncValue();
23044             this.inputEl().removeClass(['hide', 'x-hidden']);
23045             this.inputEl().dom.removeAttribute('tabIndex');
23046             this.inputEl().focus();
23047         }else{
23048             Roo.log('editor - hiding textarea');
23049 //            Roo.log('out')
23050 //            Roo.log(this.pushValue()); 
23051             this.pushValue();
23052             
23053             this.inputEl().addClass(['hide', 'x-hidden']);
23054             this.inputEl().dom.setAttribute('tabIndex', -1);
23055             //this.deferFocus();
23056         }
23057          
23058         if(this.resizable){
23059             this.setSize(this.wrap.getSize());
23060         }
23061         
23062         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23063     },
23064  
23065     // private (for BoxComponent)
23066     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23067
23068     // private (for BoxComponent)
23069     getResizeEl : function(){
23070         return this.wrap;
23071     },
23072
23073     // private (for BoxComponent)
23074     getPositionEl : function(){
23075         return this.wrap;
23076     },
23077
23078     // private
23079     initEvents : function(){
23080         this.originalValue = this.getValue();
23081     },
23082
23083 //    /**
23084 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23085 //     * @method
23086 //     */
23087 //    markInvalid : Roo.emptyFn,
23088 //    /**
23089 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23090 //     * @method
23091 //     */
23092 //    clearInvalid : Roo.emptyFn,
23093
23094     setValue : function(v){
23095         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23096         this.editorcore.pushValue();
23097     },
23098
23099      
23100     // private
23101     deferFocus : function(){
23102         this.focus.defer(10, this);
23103     },
23104
23105     // doc'ed in Field
23106     focus : function(){
23107         this.editorcore.focus();
23108         
23109     },
23110       
23111
23112     // private
23113     onDestroy : function(){
23114         
23115         
23116         
23117         if(this.rendered){
23118             
23119             for (var i =0; i < this.toolbars.length;i++) {
23120                 // fixme - ask toolbars for heights?
23121                 this.toolbars[i].onDestroy();
23122             }
23123             
23124             this.wrap.dom.innerHTML = '';
23125             this.wrap.remove();
23126         }
23127     },
23128
23129     // private
23130     onFirstFocus : function(){
23131         //Roo.log("onFirstFocus");
23132         this.editorcore.onFirstFocus();
23133          for (var i =0; i < this.toolbars.length;i++) {
23134             this.toolbars[i].onFirstFocus();
23135         }
23136         
23137     },
23138     
23139     // private
23140     syncValue : function()
23141     {   
23142         this.editorcore.syncValue();
23143     },
23144     
23145     pushValue : function()
23146     {   
23147         this.editorcore.pushValue();
23148     }
23149      
23150     
23151     // hide stuff that is not compatible
23152     /**
23153      * @event blur
23154      * @hide
23155      */
23156     /**
23157      * @event change
23158      * @hide
23159      */
23160     /**
23161      * @event focus
23162      * @hide
23163      */
23164     /**
23165      * @event specialkey
23166      * @hide
23167      */
23168     /**
23169      * @cfg {String} fieldClass @hide
23170      */
23171     /**
23172      * @cfg {String} focusClass @hide
23173      */
23174     /**
23175      * @cfg {String} autoCreate @hide
23176      */
23177     /**
23178      * @cfg {String} inputType @hide
23179      */
23180     /**
23181      * @cfg {String} invalidClass @hide
23182      */
23183     /**
23184      * @cfg {String} invalidText @hide
23185      */
23186     /**
23187      * @cfg {String} msgFx @hide
23188      */
23189     /**
23190      * @cfg {String} validateOnBlur @hide
23191      */
23192 });
23193  
23194     
23195    
23196    
23197    
23198       
23199 Roo.namespace('Roo.bootstrap.htmleditor');
23200 /**
23201  * @class Roo.bootstrap.HtmlEditorToolbar1
23202  * Basic Toolbar
23203  * 
23204  * Usage:
23205  *
23206  new Roo.bootstrap.HtmlEditor({
23207     ....
23208     toolbars : [
23209         new Roo.bootstrap.HtmlEditorToolbar1({
23210             disable : { fonts: 1 , format: 1, ..., ... , ...],
23211             btns : [ .... ]
23212         })
23213     }
23214      
23215  * 
23216  * @cfg {Object} disable List of elements to disable..
23217  * @cfg {Array} btns List of additional buttons.
23218  * 
23219  * 
23220  * NEEDS Extra CSS? 
23221  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23222  */
23223  
23224 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23225 {
23226     
23227     Roo.apply(this, config);
23228     
23229     // default disabled, based on 'good practice'..
23230     this.disable = this.disable || {};
23231     Roo.applyIf(this.disable, {
23232         fontSize : true,
23233         colors : true,
23234         specialElements : true
23235     });
23236     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23237     
23238     this.editor = config.editor;
23239     this.editorcore = config.editor.editorcore;
23240     
23241     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23242     
23243     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23244     // dont call parent... till later.
23245 }
23246 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23247      
23248     bar : true,
23249     
23250     editor : false,
23251     editorcore : false,
23252     
23253     
23254     formats : [
23255         "p" ,  
23256         "h1","h2","h3","h4","h5","h6", 
23257         "pre", "code", 
23258         "abbr", "acronym", "address", "cite", "samp", "var",
23259         'div','span'
23260     ],
23261     
23262     onRender : function(ct, position)
23263     {
23264        // Roo.log("Call onRender: " + this.xtype);
23265         
23266        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23267        Roo.log(this.el);
23268        this.el.dom.style.marginBottom = '0';
23269        var _this = this;
23270        var editorcore = this.editorcore;
23271        var editor= this.editor;
23272        
23273        var children = [];
23274        var btn = function(id,cmd , toggle, handler, html){
23275        
23276             var  event = toggle ? 'toggle' : 'click';
23277        
23278             var a = {
23279                 size : 'sm',
23280                 xtype: 'Button',
23281                 xns: Roo.bootstrap,
23282                 glyphicon : id,
23283                 cmd : id || cmd,
23284                 enableToggle:toggle !== false,
23285                 html : html || '',
23286                 pressed : toggle ? false : null,
23287                 listeners : {}
23288             };
23289             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23290                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23291             };
23292             children.push(a);
23293             return a;
23294        }
23295        
23296     //    var cb_box = function...
23297         
23298         var style = {
23299                 xtype: 'Button',
23300                 size : 'sm',
23301                 xns: Roo.bootstrap,
23302                 glyphicon : 'font',
23303                 //html : 'submit'
23304                 menu : {
23305                     xtype: 'Menu',
23306                     xns: Roo.bootstrap,
23307                     items:  []
23308                 }
23309         };
23310         Roo.each(this.formats, function(f) {
23311             style.menu.items.push({
23312                 xtype :'MenuItem',
23313                 xns: Roo.bootstrap,
23314                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23315                 tagname : f,
23316                 listeners : {
23317                     click : function()
23318                     {
23319                         editorcore.insertTag(this.tagname);
23320                         editor.focus();
23321                     }
23322                 }
23323                 
23324             });
23325         });
23326         children.push(style);   
23327         
23328         btn('bold',false,true);
23329         btn('italic',false,true);
23330         btn('align-left', 'justifyleft',true);
23331         btn('align-center', 'justifycenter',true);
23332         btn('align-right' , 'justifyright',true);
23333         btn('link', false, false, function(btn) {
23334             //Roo.log("create link?");
23335             var url = prompt(this.createLinkText, this.defaultLinkValue);
23336             if(url && url != 'http:/'+'/'){
23337                 this.editorcore.relayCmd('createlink', url);
23338             }
23339         }),
23340         btn('list','insertunorderedlist',true);
23341         btn('pencil', false,true, function(btn){
23342                 Roo.log(this);
23343                 this.toggleSourceEdit(btn.pressed);
23344         });
23345         
23346         if (this.editor.btns.length > 0) {
23347             for (var i = 0; i<this.editor.btns.length; i++) {
23348                 children.push(this.editor.btns[i]);
23349             }
23350         }
23351         
23352         /*
23353         var cog = {
23354                 xtype: 'Button',
23355                 size : 'sm',
23356                 xns: Roo.bootstrap,
23357                 glyphicon : 'cog',
23358                 //html : 'submit'
23359                 menu : {
23360                     xtype: 'Menu',
23361                     xns: Roo.bootstrap,
23362                     items:  []
23363                 }
23364         };
23365         
23366         cog.menu.items.push({
23367             xtype :'MenuItem',
23368             xns: Roo.bootstrap,
23369             html : Clean styles,
23370             tagname : f,
23371             listeners : {
23372                 click : function()
23373                 {
23374                     editorcore.insertTag(this.tagname);
23375                     editor.focus();
23376                 }
23377             }
23378             
23379         });
23380        */
23381         
23382          
23383        this.xtype = 'NavSimplebar';
23384         
23385         for(var i=0;i< children.length;i++) {
23386             
23387             this.buttons.add(this.addxtypeChild(children[i]));
23388             
23389         }
23390         
23391         editor.on('editorevent', this.updateToolbar, this);
23392     },
23393     onBtnClick : function(id)
23394     {
23395        this.editorcore.relayCmd(id);
23396        this.editorcore.focus();
23397     },
23398     
23399     /**
23400      * Protected method that will not generally be called directly. It triggers
23401      * a toolbar update by reading the markup state of the current selection in the editor.
23402      */
23403     updateToolbar: function(){
23404
23405         if(!this.editorcore.activated){
23406             this.editor.onFirstFocus(); // is this neeed?
23407             return;
23408         }
23409
23410         var btns = this.buttons; 
23411         var doc = this.editorcore.doc;
23412         btns.get('bold').setActive(doc.queryCommandState('bold'));
23413         btns.get('italic').setActive(doc.queryCommandState('italic'));
23414         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23415         
23416         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23417         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23418         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23419         
23420         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23421         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23422          /*
23423         
23424         var ans = this.editorcore.getAllAncestors();
23425         if (this.formatCombo) {
23426             
23427             
23428             var store = this.formatCombo.store;
23429             this.formatCombo.setValue("");
23430             for (var i =0; i < ans.length;i++) {
23431                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23432                     // select it..
23433                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23434                     break;
23435                 }
23436             }
23437         }
23438         
23439         
23440         
23441         // hides menus... - so this cant be on a menu...
23442         Roo.bootstrap.MenuMgr.hideAll();
23443         */
23444         Roo.bootstrap.MenuMgr.hideAll();
23445         //this.editorsyncValue();
23446     },
23447     onFirstFocus: function() {
23448         this.buttons.each(function(item){
23449            item.enable();
23450         });
23451     },
23452     toggleSourceEdit : function(sourceEditMode){
23453         
23454           
23455         if(sourceEditMode){
23456             Roo.log("disabling buttons");
23457            this.buttons.each( function(item){
23458                 if(item.cmd != 'pencil'){
23459                     item.disable();
23460                 }
23461             });
23462           
23463         }else{
23464             Roo.log("enabling buttons");
23465             if(this.editorcore.initialized){
23466                 this.buttons.each( function(item){
23467                     item.enable();
23468                 });
23469             }
23470             
23471         }
23472         Roo.log("calling toggole on editor");
23473         // tell the editor that it's been pressed..
23474         this.editor.toggleSourceEdit(sourceEditMode);
23475        
23476     }
23477 });
23478
23479
23480
23481
23482
23483 /**
23484  * @class Roo.bootstrap.Table.AbstractSelectionModel
23485  * @extends Roo.util.Observable
23486  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23487  * implemented by descendant classes.  This class should not be directly instantiated.
23488  * @constructor
23489  */
23490 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23491     this.locked = false;
23492     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23493 };
23494
23495
23496 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23497     /** @ignore Called by the grid automatically. Do not call directly. */
23498     init : function(grid){
23499         this.grid = grid;
23500         this.initEvents();
23501     },
23502
23503     /**
23504      * Locks the selections.
23505      */
23506     lock : function(){
23507         this.locked = true;
23508     },
23509
23510     /**
23511      * Unlocks the selections.
23512      */
23513     unlock : function(){
23514         this.locked = false;
23515     },
23516
23517     /**
23518      * Returns true if the selections are locked.
23519      * @return {Boolean}
23520      */
23521     isLocked : function(){
23522         return this.locked;
23523     }
23524 });
23525 /**
23526  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23527  * @class Roo.bootstrap.Table.RowSelectionModel
23528  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23529  * It supports multiple selections and keyboard selection/navigation. 
23530  * @constructor
23531  * @param {Object} config
23532  */
23533
23534 Roo.bootstrap.Table.RowSelectionModel = function(config){
23535     Roo.apply(this, config);
23536     this.selections = new Roo.util.MixedCollection(false, function(o){
23537         return o.id;
23538     });
23539
23540     this.last = false;
23541     this.lastActive = false;
23542
23543     this.addEvents({
23544         /**
23545              * @event selectionchange
23546              * Fires when the selection changes
23547              * @param {SelectionModel} this
23548              */
23549             "selectionchange" : true,
23550         /**
23551              * @event afterselectionchange
23552              * Fires after the selection changes (eg. by key press or clicking)
23553              * @param {SelectionModel} this
23554              */
23555             "afterselectionchange" : true,
23556         /**
23557              * @event beforerowselect
23558              * Fires when a row is selected being selected, return false to cancel.
23559              * @param {SelectionModel} this
23560              * @param {Number} rowIndex The selected index
23561              * @param {Boolean} keepExisting False if other selections will be cleared
23562              */
23563             "beforerowselect" : true,
23564         /**
23565              * @event rowselect
23566              * Fires when a row is selected.
23567              * @param {SelectionModel} this
23568              * @param {Number} rowIndex The selected index
23569              * @param {Roo.data.Record} r The record
23570              */
23571             "rowselect" : true,
23572         /**
23573              * @event rowdeselect
23574              * Fires when a row is deselected.
23575              * @param {SelectionModel} this
23576              * @param {Number} rowIndex The selected index
23577              */
23578         "rowdeselect" : true
23579     });
23580     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23581     this.locked = false;
23582  };
23583
23584 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23585     /**
23586      * @cfg {Boolean} singleSelect
23587      * True to allow selection of only one row at a time (defaults to false)
23588      */
23589     singleSelect : false,
23590
23591     // private
23592     initEvents : function()
23593     {
23594
23595         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23596         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23597         //}else{ // allow click to work like normal
23598          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23599         //}
23600         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23601         this.grid.on("rowclick", this.handleMouseDown, this);
23602         
23603         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23604             "up" : function(e){
23605                 if(!e.shiftKey){
23606                     this.selectPrevious(e.shiftKey);
23607                 }else if(this.last !== false && this.lastActive !== false){
23608                     var last = this.last;
23609                     this.selectRange(this.last,  this.lastActive-1);
23610                     this.grid.getView().focusRow(this.lastActive);
23611                     if(last !== false){
23612                         this.last = last;
23613                     }
23614                 }else{
23615                     this.selectFirstRow();
23616                 }
23617                 this.fireEvent("afterselectionchange", this);
23618             },
23619             "down" : function(e){
23620                 if(!e.shiftKey){
23621                     this.selectNext(e.shiftKey);
23622                 }else if(this.last !== false && this.lastActive !== false){
23623                     var last = this.last;
23624                     this.selectRange(this.last,  this.lastActive+1);
23625                     this.grid.getView().focusRow(this.lastActive);
23626                     if(last !== false){
23627                         this.last = last;
23628                     }
23629                 }else{
23630                     this.selectFirstRow();
23631                 }
23632                 this.fireEvent("afterselectionchange", this);
23633             },
23634             scope: this
23635         });
23636         this.grid.store.on('load', function(){
23637             this.selections.clear();
23638         },this);
23639         /*
23640         var view = this.grid.view;
23641         view.on("refresh", this.onRefresh, this);
23642         view.on("rowupdated", this.onRowUpdated, this);
23643         view.on("rowremoved", this.onRemove, this);
23644         */
23645     },
23646
23647     // private
23648     onRefresh : function()
23649     {
23650         var ds = this.grid.store, i, v = this.grid.view;
23651         var s = this.selections;
23652         s.each(function(r){
23653             if((i = ds.indexOfId(r.id)) != -1){
23654                 v.onRowSelect(i);
23655             }else{
23656                 s.remove(r);
23657             }
23658         });
23659     },
23660
23661     // private
23662     onRemove : function(v, index, r){
23663         this.selections.remove(r);
23664     },
23665
23666     // private
23667     onRowUpdated : function(v, index, r){
23668         if(this.isSelected(r)){
23669             v.onRowSelect(index);
23670         }
23671     },
23672
23673     /**
23674      * Select records.
23675      * @param {Array} records The records to select
23676      * @param {Boolean} keepExisting (optional) True to keep existing selections
23677      */
23678     selectRecords : function(records, keepExisting)
23679     {
23680         if(!keepExisting){
23681             this.clearSelections();
23682         }
23683             var ds = this.grid.store;
23684         for(var i = 0, len = records.length; i < len; i++){
23685             this.selectRow(ds.indexOf(records[i]), true);
23686         }
23687     },
23688
23689     /**
23690      * Gets the number of selected rows.
23691      * @return {Number}
23692      */
23693     getCount : function(){
23694         return this.selections.length;
23695     },
23696
23697     /**
23698      * Selects the first row in the grid.
23699      */
23700     selectFirstRow : function(){
23701         this.selectRow(0);
23702     },
23703
23704     /**
23705      * Select the last row.
23706      * @param {Boolean} keepExisting (optional) True to keep existing selections
23707      */
23708     selectLastRow : function(keepExisting){
23709         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23710         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23711     },
23712
23713     /**
23714      * Selects the row immediately following the last selected row.
23715      * @param {Boolean} keepExisting (optional) True to keep existing selections
23716      */
23717     selectNext : function(keepExisting)
23718     {
23719             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23720             this.selectRow(this.last+1, keepExisting);
23721             this.grid.getView().focusRow(this.last);
23722         }
23723     },
23724
23725     /**
23726      * Selects the row that precedes the last selected row.
23727      * @param {Boolean} keepExisting (optional) True to keep existing selections
23728      */
23729     selectPrevious : function(keepExisting){
23730         if(this.last){
23731             this.selectRow(this.last-1, keepExisting);
23732             this.grid.getView().focusRow(this.last);
23733         }
23734     },
23735
23736     /**
23737      * Returns the selected records
23738      * @return {Array} Array of selected records
23739      */
23740     getSelections : function(){
23741         return [].concat(this.selections.items);
23742     },
23743
23744     /**
23745      * Returns the first selected record.
23746      * @return {Record}
23747      */
23748     getSelected : function(){
23749         return this.selections.itemAt(0);
23750     },
23751
23752
23753     /**
23754      * Clears all selections.
23755      */
23756     clearSelections : function(fast)
23757     {
23758         if(this.locked) {
23759             return;
23760         }
23761         if(fast !== true){
23762                 var ds = this.grid.store;
23763             var s = this.selections;
23764             s.each(function(r){
23765                 this.deselectRow(ds.indexOfId(r.id));
23766             }, this);
23767             s.clear();
23768         }else{
23769             this.selections.clear();
23770         }
23771         this.last = false;
23772     },
23773
23774
23775     /**
23776      * Selects all rows.
23777      */
23778     selectAll : function(){
23779         if(this.locked) {
23780             return;
23781         }
23782         this.selections.clear();
23783         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23784             this.selectRow(i, true);
23785         }
23786     },
23787
23788     /**
23789      * Returns True if there is a selection.
23790      * @return {Boolean}
23791      */
23792     hasSelection : function(){
23793         return this.selections.length > 0;
23794     },
23795
23796     /**
23797      * Returns True if the specified row is selected.
23798      * @param {Number/Record} record The record or index of the record to check
23799      * @return {Boolean}
23800      */
23801     isSelected : function(index){
23802             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23803         return (r && this.selections.key(r.id) ? true : false);
23804     },
23805
23806     /**
23807      * Returns True if the specified record id is selected.
23808      * @param {String} id The id of record to check
23809      * @return {Boolean}
23810      */
23811     isIdSelected : function(id){
23812         return (this.selections.key(id) ? true : false);
23813     },
23814
23815
23816     // private
23817     handleMouseDBClick : function(e, t){
23818         
23819     },
23820     // private
23821     handleMouseDown : function(e, t)
23822     {
23823             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23824         if(this.isLocked() || rowIndex < 0 ){
23825             return;
23826         };
23827         if(e.shiftKey && this.last !== false){
23828             var last = this.last;
23829             this.selectRange(last, rowIndex, e.ctrlKey);
23830             this.last = last; // reset the last
23831             t.focus();
23832     
23833         }else{
23834             var isSelected = this.isSelected(rowIndex);
23835             //Roo.log("select row:" + rowIndex);
23836             if(isSelected){
23837                 this.deselectRow(rowIndex);
23838             } else {
23839                         this.selectRow(rowIndex, true);
23840             }
23841     
23842             /*
23843                 if(e.button !== 0 && isSelected){
23844                 alert('rowIndex 2: ' + rowIndex);
23845                     view.focusRow(rowIndex);
23846                 }else if(e.ctrlKey && isSelected){
23847                     this.deselectRow(rowIndex);
23848                 }else if(!isSelected){
23849                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23850                     view.focusRow(rowIndex);
23851                 }
23852             */
23853         }
23854         this.fireEvent("afterselectionchange", this);
23855     },
23856     // private
23857     handleDragableRowClick :  function(grid, rowIndex, e) 
23858     {
23859         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23860             this.selectRow(rowIndex, false);
23861             grid.view.focusRow(rowIndex);
23862              this.fireEvent("afterselectionchange", this);
23863         }
23864     },
23865     
23866     /**
23867      * Selects multiple rows.
23868      * @param {Array} rows Array of the indexes of the row to select
23869      * @param {Boolean} keepExisting (optional) True to keep existing selections
23870      */
23871     selectRows : function(rows, keepExisting){
23872         if(!keepExisting){
23873             this.clearSelections();
23874         }
23875         for(var i = 0, len = rows.length; i < len; i++){
23876             this.selectRow(rows[i], true);
23877         }
23878     },
23879
23880     /**
23881      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23882      * @param {Number} startRow The index of the first row in the range
23883      * @param {Number} endRow The index of the last row in the range
23884      * @param {Boolean} keepExisting (optional) True to retain existing selections
23885      */
23886     selectRange : function(startRow, endRow, keepExisting){
23887         if(this.locked) {
23888             return;
23889         }
23890         if(!keepExisting){
23891             this.clearSelections();
23892         }
23893         if(startRow <= endRow){
23894             for(var i = startRow; i <= endRow; i++){
23895                 this.selectRow(i, true);
23896             }
23897         }else{
23898             for(var i = startRow; i >= endRow; i--){
23899                 this.selectRow(i, true);
23900             }
23901         }
23902     },
23903
23904     /**
23905      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23906      * @param {Number} startRow The index of the first row in the range
23907      * @param {Number} endRow The index of the last row in the range
23908      */
23909     deselectRange : function(startRow, endRow, preventViewNotify){
23910         if(this.locked) {
23911             return;
23912         }
23913         for(var i = startRow; i <= endRow; i++){
23914             this.deselectRow(i, preventViewNotify);
23915         }
23916     },
23917
23918     /**
23919      * Selects a row.
23920      * @param {Number} row The index of the row to select
23921      * @param {Boolean} keepExisting (optional) True to keep existing selections
23922      */
23923     selectRow : function(index, keepExisting, preventViewNotify)
23924     {
23925             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23926             return;
23927         }
23928         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23929             if(!keepExisting || this.singleSelect){
23930                 this.clearSelections();
23931             }
23932             
23933             var r = this.grid.store.getAt(index);
23934             //console.log('selectRow - record id :' + r.id);
23935             
23936             this.selections.add(r);
23937             this.last = this.lastActive = index;
23938             if(!preventViewNotify){
23939                 var proxy = new Roo.Element(
23940                                 this.grid.getRowDom(index)
23941                 );
23942                 proxy.addClass('bg-info info');
23943             }
23944             this.fireEvent("rowselect", this, index, r);
23945             this.fireEvent("selectionchange", this);
23946         }
23947     },
23948
23949     /**
23950      * Deselects a row.
23951      * @param {Number} row The index of the row to deselect
23952      */
23953     deselectRow : function(index, preventViewNotify)
23954     {
23955         if(this.locked) {
23956             return;
23957         }
23958         if(this.last == index){
23959             this.last = false;
23960         }
23961         if(this.lastActive == index){
23962             this.lastActive = false;
23963         }
23964         
23965         var r = this.grid.store.getAt(index);
23966         if (!r) {
23967             return;
23968         }
23969         
23970         this.selections.remove(r);
23971         //.console.log('deselectRow - record id :' + r.id);
23972         if(!preventViewNotify){
23973         
23974             var proxy = new Roo.Element(
23975                 this.grid.getRowDom(index)
23976             );
23977             proxy.removeClass('bg-info info');
23978         }
23979         this.fireEvent("rowdeselect", this, index);
23980         this.fireEvent("selectionchange", this);
23981     },
23982
23983     // private
23984     restoreLast : function(){
23985         if(this._last){
23986             this.last = this._last;
23987         }
23988     },
23989
23990     // private
23991     acceptsNav : function(row, col, cm){
23992         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23993     },
23994
23995     // private
23996     onEditorKey : function(field, e){
23997         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23998         if(k == e.TAB){
23999             e.stopEvent();
24000             ed.completeEdit();
24001             if(e.shiftKey){
24002                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24003             }else{
24004                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24005             }
24006         }else if(k == e.ENTER && !e.ctrlKey){
24007             e.stopEvent();
24008             ed.completeEdit();
24009             if(e.shiftKey){
24010                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24011             }else{
24012                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24013             }
24014         }else if(k == e.ESC){
24015             ed.cancelEdit();
24016         }
24017         if(newCell){
24018             g.startEditing(newCell[0], newCell[1]);
24019         }
24020     }
24021 });
24022 /*
24023  * Based on:
24024  * Ext JS Library 1.1.1
24025  * Copyright(c) 2006-2007, Ext JS, LLC.
24026  *
24027  * Originally Released Under LGPL - original licence link has changed is not relivant.
24028  *
24029  * Fork - LGPL
24030  * <script type="text/javascript">
24031  */
24032  
24033 /**
24034  * @class Roo.bootstrap.PagingToolbar
24035  * @extends Roo.bootstrap.NavSimplebar
24036  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24037  * @constructor
24038  * Create a new PagingToolbar
24039  * @param {Object} config The config object
24040  * @param {Roo.data.Store} store
24041  */
24042 Roo.bootstrap.PagingToolbar = function(config)
24043 {
24044     // old args format still supported... - xtype is prefered..
24045         // created from xtype...
24046     
24047     this.ds = config.dataSource;
24048     
24049     if (config.store && !this.ds) {
24050         this.store= Roo.factory(config.store, Roo.data);
24051         this.ds = this.store;
24052         this.ds.xmodule = this.xmodule || false;
24053     }
24054     
24055     this.toolbarItems = [];
24056     if (config.items) {
24057         this.toolbarItems = config.items;
24058     }
24059     
24060     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24061     
24062     this.cursor = 0;
24063     
24064     if (this.ds) { 
24065         this.bind(this.ds);
24066     }
24067     
24068     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24069     
24070 };
24071
24072 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24073     /**
24074      * @cfg {Roo.data.Store} dataSource
24075      * The underlying data store providing the paged data
24076      */
24077     /**
24078      * @cfg {String/HTMLElement/Element} container
24079      * container The id or element that will contain the toolbar
24080      */
24081     /**
24082      * @cfg {Boolean} displayInfo
24083      * True to display the displayMsg (defaults to false)
24084      */
24085     /**
24086      * @cfg {Number} pageSize
24087      * The number of records to display per page (defaults to 20)
24088      */
24089     pageSize: 20,
24090     /**
24091      * @cfg {String} displayMsg
24092      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24093      */
24094     displayMsg : 'Displaying {0} - {1} of {2}',
24095     /**
24096      * @cfg {String} emptyMsg
24097      * The message to display when no records are found (defaults to "No data to display")
24098      */
24099     emptyMsg : 'No data to display',
24100     /**
24101      * Customizable piece of the default paging text (defaults to "Page")
24102      * @type String
24103      */
24104     beforePageText : "Page",
24105     /**
24106      * Customizable piece of the default paging text (defaults to "of %0")
24107      * @type String
24108      */
24109     afterPageText : "of {0}",
24110     /**
24111      * Customizable piece of the default paging text (defaults to "First Page")
24112      * @type String
24113      */
24114     firstText : "First Page",
24115     /**
24116      * Customizable piece of the default paging text (defaults to "Previous Page")
24117      * @type String
24118      */
24119     prevText : "Previous Page",
24120     /**
24121      * Customizable piece of the default paging text (defaults to "Next Page")
24122      * @type String
24123      */
24124     nextText : "Next Page",
24125     /**
24126      * Customizable piece of the default paging text (defaults to "Last Page")
24127      * @type String
24128      */
24129     lastText : "Last Page",
24130     /**
24131      * Customizable piece of the default paging text (defaults to "Refresh")
24132      * @type String
24133      */
24134     refreshText : "Refresh",
24135
24136     buttons : false,
24137     // private
24138     onRender : function(ct, position) 
24139     {
24140         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24141         this.navgroup.parentId = this.id;
24142         this.navgroup.onRender(this.el, null);
24143         // add the buttons to the navgroup
24144         
24145         if(this.displayInfo){
24146             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24147             this.displayEl = this.el.select('.x-paging-info', true).first();
24148 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24149 //            this.displayEl = navel.el.select('span',true).first();
24150         }
24151         
24152         var _this = this;
24153         
24154         if(this.buttons){
24155             Roo.each(_this.buttons, function(e){ // this might need to use render????
24156                Roo.factory(e).onRender(_this.el, null);
24157             });
24158         }
24159             
24160         Roo.each(_this.toolbarItems, function(e) {
24161             _this.navgroup.addItem(e);
24162         });
24163         
24164         
24165         this.first = this.navgroup.addItem({
24166             tooltip: this.firstText,
24167             cls: "prev",
24168             icon : 'fa fa-backward',
24169             disabled: true,
24170             preventDefault: true,
24171             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24172         });
24173         
24174         this.prev =  this.navgroup.addItem({
24175             tooltip: this.prevText,
24176             cls: "prev",
24177             icon : 'fa fa-step-backward',
24178             disabled: true,
24179             preventDefault: true,
24180             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24181         });
24182     //this.addSeparator();
24183         
24184         
24185         var field = this.navgroup.addItem( {
24186             tagtype : 'span',
24187             cls : 'x-paging-position',
24188             
24189             html : this.beforePageText  +
24190                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24191                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24192          } ); //?? escaped?
24193         
24194         this.field = field.el.select('input', true).first();
24195         this.field.on("keydown", this.onPagingKeydown, this);
24196         this.field.on("focus", function(){this.dom.select();});
24197     
24198     
24199         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24200         //this.field.setHeight(18);
24201         //this.addSeparator();
24202         this.next = this.navgroup.addItem({
24203             tooltip: this.nextText,
24204             cls: "next",
24205             html : ' <i class="fa fa-step-forward">',
24206             disabled: true,
24207             preventDefault: true,
24208             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24209         });
24210         this.last = this.navgroup.addItem({
24211             tooltip: this.lastText,
24212             icon : 'fa fa-forward',
24213             cls: "next",
24214             disabled: true,
24215             preventDefault: true,
24216             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24217         });
24218     //this.addSeparator();
24219         this.loading = this.navgroup.addItem({
24220             tooltip: this.refreshText,
24221             icon: 'fa fa-refresh',
24222             preventDefault: true,
24223             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24224         });
24225         
24226     },
24227
24228     // private
24229     updateInfo : function(){
24230         if(this.displayEl){
24231             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24232             var msg = count == 0 ?
24233                 this.emptyMsg :
24234                 String.format(
24235                     this.displayMsg,
24236                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24237                 );
24238             this.displayEl.update(msg);
24239         }
24240     },
24241
24242     // private
24243     onLoad : function(ds, r, o)
24244     {
24245         this.cursor = o.params ? o.params.start : 0;
24246         var d = this.getPageData(),
24247             ap = d.activePage,
24248             ps = d.pages;
24249         
24250         
24251         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24252         this.field.dom.value = ap;
24253         this.first.setDisabled(ap == 1);
24254         this.prev.setDisabled(ap == 1);
24255         this.next.setDisabled(ap == ps);
24256         this.last.setDisabled(ap == ps);
24257         this.loading.enable();
24258         this.updateInfo();
24259     },
24260
24261     // private
24262     getPageData : function(){
24263         var total = this.ds.getTotalCount();
24264         return {
24265             total : total,
24266             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24267             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24268         };
24269     },
24270
24271     // private
24272     onLoadError : function(){
24273         this.loading.enable();
24274     },
24275
24276     // private
24277     onPagingKeydown : function(e){
24278         var k = e.getKey();
24279         var d = this.getPageData();
24280         if(k == e.RETURN){
24281             var v = this.field.dom.value, pageNum;
24282             if(!v || isNaN(pageNum = parseInt(v, 10))){
24283                 this.field.dom.value = d.activePage;
24284                 return;
24285             }
24286             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24287             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24288             e.stopEvent();
24289         }
24290         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))
24291         {
24292           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24293           this.field.dom.value = pageNum;
24294           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24295           e.stopEvent();
24296         }
24297         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24298         {
24299           var v = this.field.dom.value, pageNum; 
24300           var increment = (e.shiftKey) ? 10 : 1;
24301           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24302                 increment *= -1;
24303           }
24304           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24305             this.field.dom.value = d.activePage;
24306             return;
24307           }
24308           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24309           {
24310             this.field.dom.value = parseInt(v, 10) + increment;
24311             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24312             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24313           }
24314           e.stopEvent();
24315         }
24316     },
24317
24318     // private
24319     beforeLoad : function(){
24320         if(this.loading){
24321             this.loading.disable();
24322         }
24323     },
24324
24325     // private
24326     onClick : function(which){
24327         
24328         var ds = this.ds;
24329         if (!ds) {
24330             return;
24331         }
24332         
24333         switch(which){
24334             case "first":
24335                 ds.load({params:{start: 0, limit: this.pageSize}});
24336             break;
24337             case "prev":
24338                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24339             break;
24340             case "next":
24341                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24342             break;
24343             case "last":
24344                 var total = ds.getTotalCount();
24345                 var extra = total % this.pageSize;
24346                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24347                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24348             break;
24349             case "refresh":
24350                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24351             break;
24352         }
24353     },
24354
24355     /**
24356      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24357      * @param {Roo.data.Store} store The data store to unbind
24358      */
24359     unbind : function(ds){
24360         ds.un("beforeload", this.beforeLoad, this);
24361         ds.un("load", this.onLoad, this);
24362         ds.un("loadexception", this.onLoadError, this);
24363         ds.un("remove", this.updateInfo, this);
24364         ds.un("add", this.updateInfo, this);
24365         this.ds = undefined;
24366     },
24367
24368     /**
24369      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24370      * @param {Roo.data.Store} store The data store to bind
24371      */
24372     bind : function(ds){
24373         ds.on("beforeload", this.beforeLoad, this);
24374         ds.on("load", this.onLoad, this);
24375         ds.on("loadexception", this.onLoadError, this);
24376         ds.on("remove", this.updateInfo, this);
24377         ds.on("add", this.updateInfo, this);
24378         this.ds = ds;
24379     }
24380 });/*
24381  * - LGPL
24382  *
24383  * element
24384  * 
24385  */
24386
24387 /**
24388  * @class Roo.bootstrap.MessageBar
24389  * @extends Roo.bootstrap.Component
24390  * Bootstrap MessageBar class
24391  * @cfg {String} html contents of the MessageBar
24392  * @cfg {String} weight (info | success | warning | danger) default info
24393  * @cfg {String} beforeClass insert the bar before the given class
24394  * @cfg {Boolean} closable (true | false) default false
24395  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24396  * 
24397  * @constructor
24398  * Create a new Element
24399  * @param {Object} config The config object
24400  */
24401
24402 Roo.bootstrap.MessageBar = function(config){
24403     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24404 };
24405
24406 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24407     
24408     html: '',
24409     weight: 'info',
24410     closable: false,
24411     fixed: false,
24412     beforeClass: 'bootstrap-sticky-wrap',
24413     
24414     getAutoCreate : function(){
24415         
24416         var cfg = {
24417             tag: 'div',
24418             cls: 'alert alert-dismissable alert-' + this.weight,
24419             cn: [
24420                 {
24421                     tag: 'span',
24422                     cls: 'message',
24423                     html: this.html || ''
24424                 }
24425             ]
24426         };
24427         
24428         if(this.fixed){
24429             cfg.cls += ' alert-messages-fixed';
24430         }
24431         
24432         if(this.closable){
24433             cfg.cn.push({
24434                 tag: 'button',
24435                 cls: 'close',
24436                 html: 'x'
24437             });
24438         }
24439         
24440         return cfg;
24441     },
24442     
24443     onRender : function(ct, position)
24444     {
24445         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24446         
24447         if(!this.el){
24448             var cfg = Roo.apply({},  this.getAutoCreate());
24449             cfg.id = Roo.id();
24450             
24451             if (this.cls) {
24452                 cfg.cls += ' ' + this.cls;
24453             }
24454             if (this.style) {
24455                 cfg.style = this.style;
24456             }
24457             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24458             
24459             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24460         }
24461         
24462         this.el.select('>button.close').on('click', this.hide, this);
24463         
24464     },
24465     
24466     show : function()
24467     {
24468         if (!this.rendered) {
24469             this.render();
24470         }
24471         
24472         this.el.show();
24473         
24474         this.fireEvent('show', this);
24475         
24476     },
24477     
24478     hide : function()
24479     {
24480         if (!this.rendered) {
24481             this.render();
24482         }
24483         
24484         this.el.hide();
24485         
24486         this.fireEvent('hide', this);
24487     },
24488     
24489     update : function()
24490     {
24491 //        var e = this.el.dom.firstChild;
24492 //        
24493 //        if(this.closable){
24494 //            e = e.nextSibling;
24495 //        }
24496 //        
24497 //        e.data = this.html || '';
24498
24499         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24500     }
24501    
24502 });
24503
24504  
24505
24506      /*
24507  * - LGPL
24508  *
24509  * Graph
24510  * 
24511  */
24512
24513
24514 /**
24515  * @class Roo.bootstrap.Graph
24516  * @extends Roo.bootstrap.Component
24517  * Bootstrap Graph class
24518 > Prameters
24519  -sm {number} sm 4
24520  -md {number} md 5
24521  @cfg {String} graphtype  bar | vbar | pie
24522  @cfg {number} g_x coodinator | centre x (pie)
24523  @cfg {number} g_y coodinator | centre y (pie)
24524  @cfg {number} g_r radius (pie)
24525  @cfg {number} g_height height of the chart (respected by all elements in the set)
24526  @cfg {number} g_width width of the chart (respected by all elements in the set)
24527  @cfg {Object} title The title of the chart
24528     
24529  -{Array}  values
24530  -opts (object) options for the chart 
24531      o {
24532      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24533      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24534      o vgutter (number)
24535      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.
24536      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24537      o to
24538      o stretch (boolean)
24539      o }
24540  -opts (object) options for the pie
24541      o{
24542      o cut
24543      o startAngle (number)
24544      o endAngle (number)
24545      } 
24546  *
24547  * @constructor
24548  * Create a new Input
24549  * @param {Object} config The config object
24550  */
24551
24552 Roo.bootstrap.Graph = function(config){
24553     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24554     
24555     this.addEvents({
24556         // img events
24557         /**
24558          * @event click
24559          * The img click event for the img.
24560          * @param {Roo.EventObject} e
24561          */
24562         "click" : true
24563     });
24564 };
24565
24566 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24567     
24568     sm: 4,
24569     md: 5,
24570     graphtype: 'bar',
24571     g_height: 250,
24572     g_width: 400,
24573     g_x: 50,
24574     g_y: 50,
24575     g_r: 30,
24576     opts:{
24577         //g_colors: this.colors,
24578         g_type: 'soft',
24579         g_gutter: '20%'
24580
24581     },
24582     title : false,
24583
24584     getAutoCreate : function(){
24585         
24586         var cfg = {
24587             tag: 'div',
24588             html : null
24589         };
24590         
24591         
24592         return  cfg;
24593     },
24594
24595     onRender : function(ct,position){
24596         
24597         
24598         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24599         
24600         if (typeof(Raphael) == 'undefined') {
24601             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24602             return;
24603         }
24604         
24605         this.raphael = Raphael(this.el.dom);
24606         
24607                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24608                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24609                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24610                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24611                 /*
24612                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24613                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24614                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24615                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24616                 
24617                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24618                 r.barchart(330, 10, 300, 220, data1);
24619                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24620                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24621                 */
24622                 
24623                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24624                 // r.barchart(30, 30, 560, 250,  xdata, {
24625                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24626                 //     axis : "0 0 1 1",
24627                 //     axisxlabels :  xdata
24628                 //     //yvalues : cols,
24629                    
24630                 // });
24631 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24632 //        
24633 //        this.load(null,xdata,{
24634 //                axis : "0 0 1 1",
24635 //                axisxlabels :  xdata
24636 //                });
24637
24638     },
24639
24640     load : function(graphtype,xdata,opts)
24641     {
24642         this.raphael.clear();
24643         if(!graphtype) {
24644             graphtype = this.graphtype;
24645         }
24646         if(!opts){
24647             opts = this.opts;
24648         }
24649         var r = this.raphael,
24650             fin = function () {
24651                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24652             },
24653             fout = function () {
24654                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24655             },
24656             pfin = function() {
24657                 this.sector.stop();
24658                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24659
24660                 if (this.label) {
24661                     this.label[0].stop();
24662                     this.label[0].attr({ r: 7.5 });
24663                     this.label[1].attr({ "font-weight": 800 });
24664                 }
24665             },
24666             pfout = function() {
24667                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24668
24669                 if (this.label) {
24670                     this.label[0].animate({ r: 5 }, 500, "bounce");
24671                     this.label[1].attr({ "font-weight": 400 });
24672                 }
24673             };
24674
24675         switch(graphtype){
24676             case 'bar':
24677                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24678                 break;
24679             case 'hbar':
24680                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24681                 break;
24682             case 'pie':
24683 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24684 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24685 //            
24686                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24687                 
24688                 break;
24689
24690         }
24691         
24692         if(this.title){
24693             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24694         }
24695         
24696     },
24697     
24698     setTitle: function(o)
24699     {
24700         this.title = o;
24701     },
24702     
24703     initEvents: function() {
24704         
24705         if(!this.href){
24706             this.el.on('click', this.onClick, this);
24707         }
24708     },
24709     
24710     onClick : function(e)
24711     {
24712         Roo.log('img onclick');
24713         this.fireEvent('click', this, e);
24714     }
24715    
24716 });
24717
24718  
24719 /*
24720  * - LGPL
24721  *
24722  * numberBox
24723  * 
24724  */
24725 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24726
24727 /**
24728  * @class Roo.bootstrap.dash.NumberBox
24729  * @extends Roo.bootstrap.Component
24730  * Bootstrap NumberBox class
24731  * @cfg {String} headline Box headline
24732  * @cfg {String} content Box content
24733  * @cfg {String} icon Box icon
24734  * @cfg {String} footer Footer text
24735  * @cfg {String} fhref Footer href
24736  * 
24737  * @constructor
24738  * Create a new NumberBox
24739  * @param {Object} config The config object
24740  */
24741
24742
24743 Roo.bootstrap.dash.NumberBox = function(config){
24744     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24745     
24746 };
24747
24748 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24749     
24750     headline : '',
24751     content : '',
24752     icon : '',
24753     footer : '',
24754     fhref : '',
24755     ficon : '',
24756     
24757     getAutoCreate : function(){
24758         
24759         var cfg = {
24760             tag : 'div',
24761             cls : 'small-box ',
24762             cn : [
24763                 {
24764                     tag : 'div',
24765                     cls : 'inner',
24766                     cn :[
24767                         {
24768                             tag : 'h3',
24769                             cls : 'roo-headline',
24770                             html : this.headline
24771                         },
24772                         {
24773                             tag : 'p',
24774                             cls : 'roo-content',
24775                             html : this.content
24776                         }
24777                     ]
24778                 }
24779             ]
24780         };
24781         
24782         if(this.icon){
24783             cfg.cn.push({
24784                 tag : 'div',
24785                 cls : 'icon',
24786                 cn :[
24787                     {
24788                         tag : 'i',
24789                         cls : 'ion ' + this.icon
24790                     }
24791                 ]
24792             });
24793         }
24794         
24795         if(this.footer){
24796             var footer = {
24797                 tag : 'a',
24798                 cls : 'small-box-footer',
24799                 href : this.fhref || '#',
24800                 html : this.footer
24801             };
24802             
24803             cfg.cn.push(footer);
24804             
24805         }
24806         
24807         return  cfg;
24808     },
24809
24810     onRender : function(ct,position){
24811         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24812
24813
24814        
24815                 
24816     },
24817
24818     setHeadline: function (value)
24819     {
24820         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24821     },
24822     
24823     setFooter: function (value, href)
24824     {
24825         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24826         
24827         if(href){
24828             this.el.select('a.small-box-footer',true).first().attr('href', href);
24829         }
24830         
24831     },
24832
24833     setContent: function (value)
24834     {
24835         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24836     },
24837
24838     initEvents: function() 
24839     {   
24840         
24841     }
24842     
24843 });
24844
24845  
24846 /*
24847  * - LGPL
24848  *
24849  * TabBox
24850  * 
24851  */
24852 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24853
24854 /**
24855  * @class Roo.bootstrap.dash.TabBox
24856  * @extends Roo.bootstrap.Component
24857  * Bootstrap TabBox class
24858  * @cfg {String} title Title of the TabBox
24859  * @cfg {String} icon Icon of the TabBox
24860  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24861  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24862  * 
24863  * @constructor
24864  * Create a new TabBox
24865  * @param {Object} config The config object
24866  */
24867
24868
24869 Roo.bootstrap.dash.TabBox = function(config){
24870     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24871     this.addEvents({
24872         // raw events
24873         /**
24874          * @event addpane
24875          * When a pane is added
24876          * @param {Roo.bootstrap.dash.TabPane} pane
24877          */
24878         "addpane" : true,
24879         /**
24880          * @event activatepane
24881          * When a pane is activated
24882          * @param {Roo.bootstrap.dash.TabPane} pane
24883          */
24884         "activatepane" : true
24885         
24886          
24887     });
24888     
24889     this.panes = [];
24890 };
24891
24892 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24893
24894     title : '',
24895     icon : false,
24896     showtabs : true,
24897     tabScrollable : false,
24898     
24899     getChildContainer : function()
24900     {
24901         return this.el.select('.tab-content', true).first();
24902     },
24903     
24904     getAutoCreate : function(){
24905         
24906         var header = {
24907             tag: 'li',
24908             cls: 'pull-left header',
24909             html: this.title,
24910             cn : []
24911         };
24912         
24913         if(this.icon){
24914             header.cn.push({
24915                 tag: 'i',
24916                 cls: 'fa ' + this.icon
24917             });
24918         }
24919         
24920         var h = {
24921             tag: 'ul',
24922             cls: 'nav nav-tabs pull-right',
24923             cn: [
24924                 header
24925             ]
24926         };
24927         
24928         if(this.tabScrollable){
24929             h = {
24930                 tag: 'div',
24931                 cls: 'tab-header',
24932                 cn: [
24933                     {
24934                         tag: 'ul',
24935                         cls: 'nav nav-tabs pull-right',
24936                         cn: [
24937                             header
24938                         ]
24939                     }
24940                 ]
24941             };
24942         }
24943         
24944         var cfg = {
24945             tag: 'div',
24946             cls: 'nav-tabs-custom',
24947             cn: [
24948                 h,
24949                 {
24950                     tag: 'div',
24951                     cls: 'tab-content no-padding',
24952                     cn: []
24953                 }
24954             ]
24955         };
24956
24957         return  cfg;
24958     },
24959     initEvents : function()
24960     {
24961         //Roo.log('add add pane handler');
24962         this.on('addpane', this.onAddPane, this);
24963     },
24964      /**
24965      * Updates the box title
24966      * @param {String} html to set the title to.
24967      */
24968     setTitle : function(value)
24969     {
24970         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24971     },
24972     onAddPane : function(pane)
24973     {
24974         this.panes.push(pane);
24975         //Roo.log('addpane');
24976         //Roo.log(pane);
24977         // tabs are rendere left to right..
24978         if(!this.showtabs){
24979             return;
24980         }
24981         
24982         var ctr = this.el.select('.nav-tabs', true).first();
24983          
24984          
24985         var existing = ctr.select('.nav-tab',true);
24986         var qty = existing.getCount();;
24987         
24988         
24989         var tab = ctr.createChild({
24990             tag : 'li',
24991             cls : 'nav-tab' + (qty ? '' : ' active'),
24992             cn : [
24993                 {
24994                     tag : 'a',
24995                     href:'#',
24996                     html : pane.title
24997                 }
24998             ]
24999         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25000         pane.tab = tab;
25001         
25002         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25003         if (!qty) {
25004             pane.el.addClass('active');
25005         }
25006         
25007                 
25008     },
25009     onTabClick : function(ev,un,ob,pane)
25010     {
25011         //Roo.log('tab - prev default');
25012         ev.preventDefault();
25013         
25014         
25015         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25016         pane.tab.addClass('active');
25017         //Roo.log(pane.title);
25018         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25019         // technically we should have a deactivate event.. but maybe add later.
25020         // and it should not de-activate the selected tab...
25021         this.fireEvent('activatepane', pane);
25022         pane.el.addClass('active');
25023         pane.fireEvent('activate');
25024         
25025         
25026     },
25027     
25028     getActivePane : function()
25029     {
25030         var r = false;
25031         Roo.each(this.panes, function(p) {
25032             if(p.el.hasClass('active')){
25033                 r = p;
25034                 return false;
25035             }
25036             
25037             return;
25038         });
25039         
25040         return r;
25041     }
25042     
25043     
25044 });
25045
25046  
25047 /*
25048  * - LGPL
25049  *
25050  * Tab pane
25051  * 
25052  */
25053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25054 /**
25055  * @class Roo.bootstrap.TabPane
25056  * @extends Roo.bootstrap.Component
25057  * Bootstrap TabPane class
25058  * @cfg {Boolean} active (false | true) Default false
25059  * @cfg {String} title title of panel
25060
25061  * 
25062  * @constructor
25063  * Create a new TabPane
25064  * @param {Object} config The config object
25065  */
25066
25067 Roo.bootstrap.dash.TabPane = function(config){
25068     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25069     
25070     this.addEvents({
25071         // raw events
25072         /**
25073          * @event activate
25074          * When a pane is activated
25075          * @param {Roo.bootstrap.dash.TabPane} pane
25076          */
25077         "activate" : true
25078          
25079     });
25080 };
25081
25082 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25083     
25084     active : false,
25085     title : '',
25086     
25087     // the tabBox that this is attached to.
25088     tab : false,
25089      
25090     getAutoCreate : function() 
25091     {
25092         var cfg = {
25093             tag: 'div',
25094             cls: 'tab-pane'
25095         };
25096         
25097         if(this.active){
25098             cfg.cls += ' active';
25099         }
25100         
25101         return cfg;
25102     },
25103     initEvents  : function()
25104     {
25105         //Roo.log('trigger add pane handler');
25106         this.parent().fireEvent('addpane', this)
25107     },
25108     
25109      /**
25110      * Updates the tab title 
25111      * @param {String} html to set the title to.
25112      */
25113     setTitle: function(str)
25114     {
25115         if (!this.tab) {
25116             return;
25117         }
25118         this.title = str;
25119         this.tab.select('a', true).first().dom.innerHTML = str;
25120         
25121     }
25122     
25123     
25124     
25125 });
25126
25127  
25128
25129
25130  /*
25131  * - LGPL
25132  *
25133  * menu
25134  * 
25135  */
25136 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25137
25138 /**
25139  * @class Roo.bootstrap.menu.Menu
25140  * @extends Roo.bootstrap.Component
25141  * Bootstrap Menu class - container for Menu
25142  * @cfg {String} html Text of the menu
25143  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25144  * @cfg {String} icon Font awesome icon
25145  * @cfg {String} pos Menu align to (top | bottom) default bottom
25146  * 
25147  * 
25148  * @constructor
25149  * Create a new Menu
25150  * @param {Object} config The config object
25151  */
25152
25153
25154 Roo.bootstrap.menu.Menu = function(config){
25155     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25156     
25157     this.addEvents({
25158         /**
25159          * @event beforeshow
25160          * Fires before this menu is displayed
25161          * @param {Roo.bootstrap.menu.Menu} this
25162          */
25163         beforeshow : true,
25164         /**
25165          * @event beforehide
25166          * Fires before this menu is hidden
25167          * @param {Roo.bootstrap.menu.Menu} this
25168          */
25169         beforehide : true,
25170         /**
25171          * @event show
25172          * Fires after this menu is displayed
25173          * @param {Roo.bootstrap.menu.Menu} this
25174          */
25175         show : true,
25176         /**
25177          * @event hide
25178          * Fires after this menu is hidden
25179          * @param {Roo.bootstrap.menu.Menu} this
25180          */
25181         hide : true,
25182         /**
25183          * @event click
25184          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25185          * @param {Roo.bootstrap.menu.Menu} this
25186          * @param {Roo.EventObject} e
25187          */
25188         click : true
25189     });
25190     
25191 };
25192
25193 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25194     
25195     submenu : false,
25196     html : '',
25197     weight : 'default',
25198     icon : false,
25199     pos : 'bottom',
25200     
25201     
25202     getChildContainer : function() {
25203         if(this.isSubMenu){
25204             return this.el;
25205         }
25206         
25207         return this.el.select('ul.dropdown-menu', true).first();  
25208     },
25209     
25210     getAutoCreate : function()
25211     {
25212         var text = [
25213             {
25214                 tag : 'span',
25215                 cls : 'roo-menu-text',
25216                 html : this.html
25217             }
25218         ];
25219         
25220         if(this.icon){
25221             text.unshift({
25222                 tag : 'i',
25223                 cls : 'fa ' + this.icon
25224             })
25225         }
25226         
25227         
25228         var cfg = {
25229             tag : 'div',
25230             cls : 'btn-group',
25231             cn : [
25232                 {
25233                     tag : 'button',
25234                     cls : 'dropdown-button btn btn-' + this.weight,
25235                     cn : text
25236                 },
25237                 {
25238                     tag : 'button',
25239                     cls : 'dropdown-toggle btn btn-' + this.weight,
25240                     cn : [
25241                         {
25242                             tag : 'span',
25243                             cls : 'caret'
25244                         }
25245                     ]
25246                 },
25247                 {
25248                     tag : 'ul',
25249                     cls : 'dropdown-menu'
25250                 }
25251             ]
25252             
25253         };
25254         
25255         if(this.pos == 'top'){
25256             cfg.cls += ' dropup';
25257         }
25258         
25259         if(this.isSubMenu){
25260             cfg = {
25261                 tag : 'ul',
25262                 cls : 'dropdown-menu'
25263             }
25264         }
25265         
25266         return cfg;
25267     },
25268     
25269     onRender : function(ct, position)
25270     {
25271         this.isSubMenu = ct.hasClass('dropdown-submenu');
25272         
25273         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25274     },
25275     
25276     initEvents : function() 
25277     {
25278         if(this.isSubMenu){
25279             return;
25280         }
25281         
25282         this.hidden = true;
25283         
25284         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25285         this.triggerEl.on('click', this.onTriggerPress, this);
25286         
25287         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25288         this.buttonEl.on('click', this.onClick, this);
25289         
25290     },
25291     
25292     list : function()
25293     {
25294         if(this.isSubMenu){
25295             return this.el;
25296         }
25297         
25298         return this.el.select('ul.dropdown-menu', true).first();
25299     },
25300     
25301     onClick : function(e)
25302     {
25303         this.fireEvent("click", this, e);
25304     },
25305     
25306     onTriggerPress  : function(e)
25307     {   
25308         if (this.isVisible()) {
25309             this.hide();
25310         } else {
25311             this.show();
25312         }
25313     },
25314     
25315     isVisible : function(){
25316         return !this.hidden;
25317     },
25318     
25319     show : function()
25320     {
25321         this.fireEvent("beforeshow", this);
25322         
25323         this.hidden = false;
25324         this.el.addClass('open');
25325         
25326         Roo.get(document).on("mouseup", this.onMouseUp, this);
25327         
25328         this.fireEvent("show", this);
25329         
25330         
25331     },
25332     
25333     hide : function()
25334     {
25335         this.fireEvent("beforehide", this);
25336         
25337         this.hidden = true;
25338         this.el.removeClass('open');
25339         
25340         Roo.get(document).un("mouseup", this.onMouseUp);
25341         
25342         this.fireEvent("hide", this);
25343     },
25344     
25345     onMouseUp : function()
25346     {
25347         this.hide();
25348     }
25349     
25350 });
25351
25352  
25353  /*
25354  * - LGPL
25355  *
25356  * menu item
25357  * 
25358  */
25359 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25360
25361 /**
25362  * @class Roo.bootstrap.menu.Item
25363  * @extends Roo.bootstrap.Component
25364  * Bootstrap MenuItem class
25365  * @cfg {Boolean} submenu (true | false) default false
25366  * @cfg {String} html text of the item
25367  * @cfg {String} href the link
25368  * @cfg {Boolean} disable (true | false) default false
25369  * @cfg {Boolean} preventDefault (true | false) default true
25370  * @cfg {String} icon Font awesome icon
25371  * @cfg {String} pos Submenu align to (left | right) default right 
25372  * 
25373  * 
25374  * @constructor
25375  * Create a new Item
25376  * @param {Object} config The config object
25377  */
25378
25379
25380 Roo.bootstrap.menu.Item = function(config){
25381     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25382     this.addEvents({
25383         /**
25384          * @event mouseover
25385          * Fires when the mouse is hovering over this menu
25386          * @param {Roo.bootstrap.menu.Item} this
25387          * @param {Roo.EventObject} e
25388          */
25389         mouseover : true,
25390         /**
25391          * @event mouseout
25392          * Fires when the mouse exits this menu
25393          * @param {Roo.bootstrap.menu.Item} this
25394          * @param {Roo.EventObject} e
25395          */
25396         mouseout : true,
25397         // raw events
25398         /**
25399          * @event click
25400          * The raw click event for the entire grid.
25401          * @param {Roo.EventObject} e
25402          */
25403         click : true
25404     });
25405 };
25406
25407 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25408     
25409     submenu : false,
25410     href : '',
25411     html : '',
25412     preventDefault: true,
25413     disable : false,
25414     icon : false,
25415     pos : 'right',
25416     
25417     getAutoCreate : function()
25418     {
25419         var text = [
25420             {
25421                 tag : 'span',
25422                 cls : 'roo-menu-item-text',
25423                 html : this.html
25424             }
25425         ];
25426         
25427         if(this.icon){
25428             text.unshift({
25429                 tag : 'i',
25430                 cls : 'fa ' + this.icon
25431             })
25432         }
25433         
25434         var cfg = {
25435             tag : 'li',
25436             cn : [
25437                 {
25438                     tag : 'a',
25439                     href : this.href || '#',
25440                     cn : text
25441                 }
25442             ]
25443         };
25444         
25445         if(this.disable){
25446             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25447         }
25448         
25449         if(this.submenu){
25450             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25451             
25452             if(this.pos == 'left'){
25453                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25454             }
25455         }
25456         
25457         return cfg;
25458     },
25459     
25460     initEvents : function() 
25461     {
25462         this.el.on('mouseover', this.onMouseOver, this);
25463         this.el.on('mouseout', this.onMouseOut, this);
25464         
25465         this.el.select('a', true).first().on('click', this.onClick, this);
25466         
25467     },
25468     
25469     onClick : function(e)
25470     {
25471         if(this.preventDefault){
25472             e.preventDefault();
25473         }
25474         
25475         this.fireEvent("click", this, e);
25476     },
25477     
25478     onMouseOver : function(e)
25479     {
25480         if(this.submenu && this.pos == 'left'){
25481             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25482         }
25483         
25484         this.fireEvent("mouseover", this, e);
25485     },
25486     
25487     onMouseOut : function(e)
25488     {
25489         this.fireEvent("mouseout", this, e);
25490     }
25491 });
25492
25493  
25494
25495  /*
25496  * - LGPL
25497  *
25498  * menu separator
25499  * 
25500  */
25501 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25502
25503 /**
25504  * @class Roo.bootstrap.menu.Separator
25505  * @extends Roo.bootstrap.Component
25506  * Bootstrap Separator class
25507  * 
25508  * @constructor
25509  * Create a new Separator
25510  * @param {Object} config The config object
25511  */
25512
25513
25514 Roo.bootstrap.menu.Separator = function(config){
25515     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25516 };
25517
25518 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25519     
25520     getAutoCreate : function(){
25521         var cfg = {
25522             tag : 'li',
25523             cls: 'divider'
25524         };
25525         
25526         return cfg;
25527     }
25528    
25529 });
25530
25531  
25532
25533  /*
25534  * - LGPL
25535  *
25536  * Tooltip
25537  * 
25538  */
25539
25540 /**
25541  * @class Roo.bootstrap.Tooltip
25542  * Bootstrap Tooltip class
25543  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25544  * to determine which dom element triggers the tooltip.
25545  * 
25546  * It needs to add support for additional attributes like tooltip-position
25547  * 
25548  * @constructor
25549  * Create a new Toolti
25550  * @param {Object} config The config object
25551  */
25552
25553 Roo.bootstrap.Tooltip = function(config){
25554     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25555     
25556     this.alignment = Roo.bootstrap.Tooltip.alignment;
25557     
25558     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25559         this.alignment = config.alignment;
25560     }
25561     
25562 };
25563
25564 Roo.apply(Roo.bootstrap.Tooltip, {
25565     /**
25566      * @function init initialize tooltip monitoring.
25567      * @static
25568      */
25569     currentEl : false,
25570     currentTip : false,
25571     currentRegion : false,
25572     
25573     //  init : delay?
25574     
25575     init : function()
25576     {
25577         Roo.get(document).on('mouseover', this.enter ,this);
25578         Roo.get(document).on('mouseout', this.leave, this);
25579          
25580         
25581         this.currentTip = new Roo.bootstrap.Tooltip();
25582     },
25583     
25584     enter : function(ev)
25585     {
25586         var dom = ev.getTarget();
25587         
25588         //Roo.log(['enter',dom]);
25589         var el = Roo.fly(dom);
25590         if (this.currentEl) {
25591             //Roo.log(dom);
25592             //Roo.log(this.currentEl);
25593             //Roo.log(this.currentEl.contains(dom));
25594             if (this.currentEl == el) {
25595                 return;
25596             }
25597             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25598                 return;
25599             }
25600
25601         }
25602         
25603         if (this.currentTip.el) {
25604             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25605         }    
25606         //Roo.log(ev);
25607         
25608         if(!el || el.dom == document){
25609             return;
25610         }
25611         
25612         var bindEl = el;
25613         
25614         // you can not look for children, as if el is the body.. then everythign is the child..
25615         if (!el.attr('tooltip')) { //
25616             if (!el.select("[tooltip]").elements.length) {
25617                 return;
25618             }
25619             // is the mouse over this child...?
25620             bindEl = el.select("[tooltip]").first();
25621             var xy = ev.getXY();
25622             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25623                 //Roo.log("not in region.");
25624                 return;
25625             }
25626             //Roo.log("child element over..");
25627             
25628         }
25629         this.currentEl = bindEl;
25630         this.currentTip.bind(bindEl);
25631         this.currentRegion = Roo.lib.Region.getRegion(dom);
25632         this.currentTip.enter();
25633         
25634     },
25635     leave : function(ev)
25636     {
25637         var dom = ev.getTarget();
25638         //Roo.log(['leave',dom]);
25639         if (!this.currentEl) {
25640             return;
25641         }
25642         
25643         
25644         if (dom != this.currentEl.dom) {
25645             return;
25646         }
25647         var xy = ev.getXY();
25648         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25649             return;
25650         }
25651         // only activate leave if mouse cursor is outside... bounding box..
25652         
25653         
25654         
25655         
25656         if (this.currentTip) {
25657             this.currentTip.leave();
25658         }
25659         //Roo.log('clear currentEl');
25660         this.currentEl = false;
25661         
25662         
25663     },
25664     alignment : {
25665         'left' : ['r-l', [-2,0], 'right'],
25666         'right' : ['l-r', [2,0], 'left'],
25667         'bottom' : ['t-b', [0,2], 'top'],
25668         'top' : [ 'b-t', [0,-2], 'bottom']
25669     }
25670     
25671 });
25672
25673
25674 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25675     
25676     
25677     bindEl : false,
25678     
25679     delay : null, // can be { show : 300 , hide: 500}
25680     
25681     timeout : null,
25682     
25683     hoverState : null, //???
25684     
25685     placement : 'bottom', 
25686     
25687     alignment : false,
25688     
25689     getAutoCreate : function(){
25690     
25691         var cfg = {
25692            cls : 'tooltip',
25693            role : 'tooltip',
25694            cn : [
25695                 {
25696                     cls : 'tooltip-arrow'
25697                 },
25698                 {
25699                     cls : 'tooltip-inner'
25700                 }
25701            ]
25702         };
25703         
25704         return cfg;
25705     },
25706     bind : function(el)
25707     {
25708         this.bindEl = el;
25709     },
25710       
25711     
25712     enter : function () {
25713        
25714         if (this.timeout != null) {
25715             clearTimeout(this.timeout);
25716         }
25717         
25718         this.hoverState = 'in';
25719          //Roo.log("enter - show");
25720         if (!this.delay || !this.delay.show) {
25721             this.show();
25722             return;
25723         }
25724         var _t = this;
25725         this.timeout = setTimeout(function () {
25726             if (_t.hoverState == 'in') {
25727                 _t.show();
25728             }
25729         }, this.delay.show);
25730     },
25731     leave : function()
25732     {
25733         clearTimeout(this.timeout);
25734     
25735         this.hoverState = 'out';
25736          if (!this.delay || !this.delay.hide) {
25737             this.hide();
25738             return;
25739         }
25740        
25741         var _t = this;
25742         this.timeout = setTimeout(function () {
25743             //Roo.log("leave - timeout");
25744             
25745             if (_t.hoverState == 'out') {
25746                 _t.hide();
25747                 Roo.bootstrap.Tooltip.currentEl = false;
25748             }
25749         }, delay);
25750     },
25751     
25752     show : function (msg)
25753     {
25754         if (!this.el) {
25755             this.render(document.body);
25756         }
25757         // set content.
25758         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25759         
25760         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25761         
25762         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25763         
25764         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25765         
25766         var placement = typeof this.placement == 'function' ?
25767             this.placement.call(this, this.el, on_el) :
25768             this.placement;
25769             
25770         var autoToken = /\s?auto?\s?/i;
25771         var autoPlace = autoToken.test(placement);
25772         if (autoPlace) {
25773             placement = placement.replace(autoToken, '') || 'top';
25774         }
25775         
25776         //this.el.detach()
25777         //this.el.setXY([0,0]);
25778         this.el.show();
25779         //this.el.dom.style.display='block';
25780         
25781         //this.el.appendTo(on_el);
25782         
25783         var p = this.getPosition();
25784         var box = this.el.getBox();
25785         
25786         if (autoPlace) {
25787             // fixme..
25788         }
25789         
25790         var align = this.alignment[placement];
25791         
25792         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25793         
25794         if(placement == 'top' || placement == 'bottom'){
25795             if(xy[0] < 0){
25796                 placement = 'right';
25797             }
25798             
25799             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25800                 placement = 'left';
25801             }
25802             
25803             var scroll = Roo.select('body', true).first().getScroll();
25804             
25805             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25806                 placement = 'top';
25807             }
25808             
25809         }
25810         
25811         this.el.alignTo(this.bindEl, align[0],align[1]);
25812         //var arrow = this.el.select('.arrow',true).first();
25813         //arrow.set(align[2], 
25814         
25815         this.el.addClass(placement);
25816         
25817         this.el.addClass('in fade');
25818         
25819         this.hoverState = null;
25820         
25821         if (this.el.hasClass('fade')) {
25822             // fade it?
25823         }
25824         
25825     },
25826     hide : function()
25827     {
25828          
25829         if (!this.el) {
25830             return;
25831         }
25832         //this.el.setXY([0,0]);
25833         this.el.removeClass('in');
25834         //this.el.hide();
25835         
25836     }
25837     
25838 });
25839  
25840
25841  /*
25842  * - LGPL
25843  *
25844  * Location Picker
25845  * 
25846  */
25847
25848 /**
25849  * @class Roo.bootstrap.LocationPicker
25850  * @extends Roo.bootstrap.Component
25851  * Bootstrap LocationPicker class
25852  * @cfg {Number} latitude Position when init default 0
25853  * @cfg {Number} longitude Position when init default 0
25854  * @cfg {Number} zoom default 15
25855  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25856  * @cfg {Boolean} mapTypeControl default false
25857  * @cfg {Boolean} disableDoubleClickZoom default false
25858  * @cfg {Boolean} scrollwheel default true
25859  * @cfg {Boolean} streetViewControl default false
25860  * @cfg {Number} radius default 0
25861  * @cfg {String} locationName
25862  * @cfg {Boolean} draggable default true
25863  * @cfg {Boolean} enableAutocomplete default false
25864  * @cfg {Boolean} enableReverseGeocode default true
25865  * @cfg {String} markerTitle
25866  * 
25867  * @constructor
25868  * Create a new LocationPicker
25869  * @param {Object} config The config object
25870  */
25871
25872
25873 Roo.bootstrap.LocationPicker = function(config){
25874     
25875     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25876     
25877     this.addEvents({
25878         /**
25879          * @event initial
25880          * Fires when the picker initialized.
25881          * @param {Roo.bootstrap.LocationPicker} this
25882          * @param {Google Location} location
25883          */
25884         initial : true,
25885         /**
25886          * @event positionchanged
25887          * Fires when the picker position changed.
25888          * @param {Roo.bootstrap.LocationPicker} this
25889          * @param {Google Location} location
25890          */
25891         positionchanged : true,
25892         /**
25893          * @event resize
25894          * Fires when the map resize.
25895          * @param {Roo.bootstrap.LocationPicker} this
25896          */
25897         resize : true,
25898         /**
25899          * @event show
25900          * Fires when the map show.
25901          * @param {Roo.bootstrap.LocationPicker} this
25902          */
25903         show : true,
25904         /**
25905          * @event hide
25906          * Fires when the map hide.
25907          * @param {Roo.bootstrap.LocationPicker} this
25908          */
25909         hide : true,
25910         /**
25911          * @event mapClick
25912          * Fires when click the map.
25913          * @param {Roo.bootstrap.LocationPicker} this
25914          * @param {Map event} e
25915          */
25916         mapClick : true,
25917         /**
25918          * @event mapRightClick
25919          * Fires when right click the map.
25920          * @param {Roo.bootstrap.LocationPicker} this
25921          * @param {Map event} e
25922          */
25923         mapRightClick : true,
25924         /**
25925          * @event markerClick
25926          * Fires when click the marker.
25927          * @param {Roo.bootstrap.LocationPicker} this
25928          * @param {Map event} e
25929          */
25930         markerClick : true,
25931         /**
25932          * @event markerRightClick
25933          * Fires when right click the marker.
25934          * @param {Roo.bootstrap.LocationPicker} this
25935          * @param {Map event} e
25936          */
25937         markerRightClick : true,
25938         /**
25939          * @event OverlayViewDraw
25940          * Fires when OverlayView Draw
25941          * @param {Roo.bootstrap.LocationPicker} this
25942          */
25943         OverlayViewDraw : true,
25944         /**
25945          * @event OverlayViewOnAdd
25946          * Fires when OverlayView Draw
25947          * @param {Roo.bootstrap.LocationPicker} this
25948          */
25949         OverlayViewOnAdd : true,
25950         /**
25951          * @event OverlayViewOnRemove
25952          * Fires when OverlayView Draw
25953          * @param {Roo.bootstrap.LocationPicker} this
25954          */
25955         OverlayViewOnRemove : true,
25956         /**
25957          * @event OverlayViewShow
25958          * Fires when OverlayView Draw
25959          * @param {Roo.bootstrap.LocationPicker} this
25960          * @param {Pixel} cpx
25961          */
25962         OverlayViewShow : true,
25963         /**
25964          * @event OverlayViewHide
25965          * Fires when OverlayView Draw
25966          * @param {Roo.bootstrap.LocationPicker} this
25967          */
25968         OverlayViewHide : true,
25969         /**
25970          * @event loadexception
25971          * Fires when load google lib failed.
25972          * @param {Roo.bootstrap.LocationPicker} this
25973          */
25974         loadexception : true
25975     });
25976         
25977 };
25978
25979 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25980     
25981     gMapContext: false,
25982     
25983     latitude: 0,
25984     longitude: 0,
25985     zoom: 15,
25986     mapTypeId: false,
25987     mapTypeControl: false,
25988     disableDoubleClickZoom: false,
25989     scrollwheel: true,
25990     streetViewControl: false,
25991     radius: 0,
25992     locationName: '',
25993     draggable: true,
25994     enableAutocomplete: false,
25995     enableReverseGeocode: true,
25996     markerTitle: '',
25997     
25998     getAutoCreate: function()
25999     {
26000
26001         var cfg = {
26002             tag: 'div',
26003             cls: 'roo-location-picker'
26004         };
26005         
26006         return cfg
26007     },
26008     
26009     initEvents: function(ct, position)
26010     {       
26011         if(!this.el.getWidth() || this.isApplied()){
26012             return;
26013         }
26014         
26015         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26016         
26017         this.initial();
26018     },
26019     
26020     initial: function()
26021     {
26022         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26023             this.fireEvent('loadexception', this);
26024             return;
26025         }
26026         
26027         if(!this.mapTypeId){
26028             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26029         }
26030         
26031         this.gMapContext = this.GMapContext();
26032         
26033         this.initOverlayView();
26034         
26035         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26036         
26037         var _this = this;
26038                 
26039         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26040             _this.setPosition(_this.gMapContext.marker.position);
26041         });
26042         
26043         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26044             _this.fireEvent('mapClick', this, event);
26045             
26046         });
26047
26048         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26049             _this.fireEvent('mapRightClick', this, event);
26050             
26051         });
26052         
26053         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26054             _this.fireEvent('markerClick', this, event);
26055             
26056         });
26057
26058         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26059             _this.fireEvent('markerRightClick', this, event);
26060             
26061         });
26062         
26063         this.setPosition(this.gMapContext.location);
26064         
26065         this.fireEvent('initial', this, this.gMapContext.location);
26066     },
26067     
26068     initOverlayView: function()
26069     {
26070         var _this = this;
26071         
26072         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26073             
26074             draw: function()
26075             {
26076                 _this.fireEvent('OverlayViewDraw', _this);
26077             },
26078             
26079             onAdd: function()
26080             {
26081                 _this.fireEvent('OverlayViewOnAdd', _this);
26082             },
26083             
26084             onRemove: function()
26085             {
26086                 _this.fireEvent('OverlayViewOnRemove', _this);
26087             },
26088             
26089             show: function(cpx)
26090             {
26091                 _this.fireEvent('OverlayViewShow', _this, cpx);
26092             },
26093             
26094             hide: function()
26095             {
26096                 _this.fireEvent('OverlayViewHide', _this);
26097             }
26098             
26099         });
26100     },
26101     
26102     fromLatLngToContainerPixel: function(event)
26103     {
26104         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26105     },
26106     
26107     isApplied: function() 
26108     {
26109         return this.getGmapContext() == false ? false : true;
26110     },
26111     
26112     getGmapContext: function() 
26113     {
26114         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26115     },
26116     
26117     GMapContext: function() 
26118     {
26119         var position = new google.maps.LatLng(this.latitude, this.longitude);
26120         
26121         var _map = new google.maps.Map(this.el.dom, {
26122             center: position,
26123             zoom: this.zoom,
26124             mapTypeId: this.mapTypeId,
26125             mapTypeControl: this.mapTypeControl,
26126             disableDoubleClickZoom: this.disableDoubleClickZoom,
26127             scrollwheel: this.scrollwheel,
26128             streetViewControl: this.streetViewControl,
26129             locationName: this.locationName,
26130             draggable: this.draggable,
26131             enableAutocomplete: this.enableAutocomplete,
26132             enableReverseGeocode: this.enableReverseGeocode
26133         });
26134         
26135         var _marker = new google.maps.Marker({
26136             position: position,
26137             map: _map,
26138             title: this.markerTitle,
26139             draggable: this.draggable
26140         });
26141         
26142         return {
26143             map: _map,
26144             marker: _marker,
26145             circle: null,
26146             location: position,
26147             radius: this.radius,
26148             locationName: this.locationName,
26149             addressComponents: {
26150                 formatted_address: null,
26151                 addressLine1: null,
26152                 addressLine2: null,
26153                 streetName: null,
26154                 streetNumber: null,
26155                 city: null,
26156                 district: null,
26157                 state: null,
26158                 stateOrProvince: null
26159             },
26160             settings: this,
26161             domContainer: this.el.dom,
26162             geodecoder: new google.maps.Geocoder()
26163         };
26164     },
26165     
26166     drawCircle: function(center, radius, options) 
26167     {
26168         if (this.gMapContext.circle != null) {
26169             this.gMapContext.circle.setMap(null);
26170         }
26171         if (radius > 0) {
26172             radius *= 1;
26173             options = Roo.apply({}, options, {
26174                 strokeColor: "#0000FF",
26175                 strokeOpacity: .35,
26176                 strokeWeight: 2,
26177                 fillColor: "#0000FF",
26178                 fillOpacity: .2
26179             });
26180             
26181             options.map = this.gMapContext.map;
26182             options.radius = radius;
26183             options.center = center;
26184             this.gMapContext.circle = new google.maps.Circle(options);
26185             return this.gMapContext.circle;
26186         }
26187         
26188         return null;
26189     },
26190     
26191     setPosition: function(location) 
26192     {
26193         this.gMapContext.location = location;
26194         this.gMapContext.marker.setPosition(location);
26195         this.gMapContext.map.panTo(location);
26196         this.drawCircle(location, this.gMapContext.radius, {});
26197         
26198         var _this = this;
26199         
26200         if (this.gMapContext.settings.enableReverseGeocode) {
26201             this.gMapContext.geodecoder.geocode({
26202                 latLng: this.gMapContext.location
26203             }, function(results, status) {
26204                 
26205                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26206                     _this.gMapContext.locationName = results[0].formatted_address;
26207                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26208                     
26209                     _this.fireEvent('positionchanged', this, location);
26210                 }
26211             });
26212             
26213             return;
26214         }
26215         
26216         this.fireEvent('positionchanged', this, location);
26217     },
26218     
26219     resize: function()
26220     {
26221         google.maps.event.trigger(this.gMapContext.map, "resize");
26222         
26223         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26224         
26225         this.fireEvent('resize', this);
26226     },
26227     
26228     setPositionByLatLng: function(latitude, longitude)
26229     {
26230         this.setPosition(new google.maps.LatLng(latitude, longitude));
26231     },
26232     
26233     getCurrentPosition: function() 
26234     {
26235         return {
26236             latitude: this.gMapContext.location.lat(),
26237             longitude: this.gMapContext.location.lng()
26238         };
26239     },
26240     
26241     getAddressName: function() 
26242     {
26243         return this.gMapContext.locationName;
26244     },
26245     
26246     getAddressComponents: function() 
26247     {
26248         return this.gMapContext.addressComponents;
26249     },
26250     
26251     address_component_from_google_geocode: function(address_components) 
26252     {
26253         var result = {};
26254         
26255         for (var i = 0; i < address_components.length; i++) {
26256             var component = address_components[i];
26257             if (component.types.indexOf("postal_code") >= 0) {
26258                 result.postalCode = component.short_name;
26259             } else if (component.types.indexOf("street_number") >= 0) {
26260                 result.streetNumber = component.short_name;
26261             } else if (component.types.indexOf("route") >= 0) {
26262                 result.streetName = component.short_name;
26263             } else if (component.types.indexOf("neighborhood") >= 0) {
26264                 result.city = component.short_name;
26265             } else if (component.types.indexOf("locality") >= 0) {
26266                 result.city = component.short_name;
26267             } else if (component.types.indexOf("sublocality") >= 0) {
26268                 result.district = component.short_name;
26269             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26270                 result.stateOrProvince = component.short_name;
26271             } else if (component.types.indexOf("country") >= 0) {
26272                 result.country = component.short_name;
26273             }
26274         }
26275         
26276         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26277         result.addressLine2 = "";
26278         return result;
26279     },
26280     
26281     setZoomLevel: function(zoom)
26282     {
26283         this.gMapContext.map.setZoom(zoom);
26284     },
26285     
26286     show: function()
26287     {
26288         if(!this.el){
26289             return;
26290         }
26291         
26292         this.el.show();
26293         
26294         this.resize();
26295         
26296         this.fireEvent('show', this);
26297     },
26298     
26299     hide: function()
26300     {
26301         if(!this.el){
26302             return;
26303         }
26304         
26305         this.el.hide();
26306         
26307         this.fireEvent('hide', this);
26308     }
26309     
26310 });
26311
26312 Roo.apply(Roo.bootstrap.LocationPicker, {
26313     
26314     OverlayView : function(map, options)
26315     {
26316         options = options || {};
26317         
26318         this.setMap(map);
26319     }
26320     
26321     
26322 });/*
26323  * - LGPL
26324  *
26325  * Alert
26326  * 
26327  */
26328
26329 /**
26330  * @class Roo.bootstrap.Alert
26331  * @extends Roo.bootstrap.Component
26332  * Bootstrap Alert class
26333  * @cfg {String} title The title of alert
26334  * @cfg {String} html The content of alert
26335  * @cfg {String} weight (  success | info | warning | danger )
26336  * @cfg {String} faicon font-awesomeicon
26337  * 
26338  * @constructor
26339  * Create a new alert
26340  * @param {Object} config The config object
26341  */
26342
26343
26344 Roo.bootstrap.Alert = function(config){
26345     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26346     
26347 };
26348
26349 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26350     
26351     title: '',
26352     html: '',
26353     weight: false,
26354     faicon: false,
26355     
26356     getAutoCreate : function()
26357     {
26358         
26359         var cfg = {
26360             tag : 'div',
26361             cls : 'alert',
26362             cn : [
26363                 {
26364                     tag : 'i',
26365                     cls : 'roo-alert-icon'
26366                     
26367                 },
26368                 {
26369                     tag : 'b',
26370                     cls : 'roo-alert-title',
26371                     html : this.title
26372                 },
26373                 {
26374                     tag : 'span',
26375                     cls : 'roo-alert-text',
26376                     html : this.html
26377                 }
26378             ]
26379         };
26380         
26381         if(this.faicon){
26382             cfg.cn[0].cls += ' fa ' + this.faicon;
26383         }
26384         
26385         if(this.weight){
26386             cfg.cls += ' alert-' + this.weight;
26387         }
26388         
26389         return cfg;
26390     },
26391     
26392     initEvents: function() 
26393     {
26394         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26395     },
26396     
26397     setTitle : function(str)
26398     {
26399         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26400     },
26401     
26402     setText : function(str)
26403     {
26404         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26405     },
26406     
26407     setWeight : function(weight)
26408     {
26409         if(this.weight){
26410             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26411         }
26412         
26413         this.weight = weight;
26414         
26415         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26416     },
26417     
26418     setIcon : function(icon)
26419     {
26420         if(this.faicon){
26421             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26422         }
26423         
26424         this.faicon = icon;
26425         
26426         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26427     },
26428     
26429     hide: function() 
26430     {
26431         this.el.hide();   
26432     },
26433     
26434     show: function() 
26435     {  
26436         this.el.show();   
26437     }
26438     
26439 });
26440
26441  
26442 /*
26443 * Licence: LGPL
26444 */
26445
26446 /**
26447  * @class Roo.bootstrap.UploadCropbox
26448  * @extends Roo.bootstrap.Component
26449  * Bootstrap UploadCropbox class
26450  * @cfg {String} emptyText show when image has been loaded
26451  * @cfg {String} rotateNotify show when image too small to rotate
26452  * @cfg {Number} errorTimeout default 3000
26453  * @cfg {Number} minWidth default 300
26454  * @cfg {Number} minHeight default 300
26455  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26456  * @cfg {Boolean} isDocument (true|false) default false
26457  * @cfg {String} url action url
26458  * @cfg {String} paramName default 'imageUpload'
26459  * @cfg {String} method default POST
26460  * @cfg {Boolean} loadMask (true|false) default true
26461  * @cfg {Boolean} loadingText default 'Loading...'
26462  * 
26463  * @constructor
26464  * Create a new UploadCropbox
26465  * @param {Object} config The config object
26466  */
26467
26468 Roo.bootstrap.UploadCropbox = function(config){
26469     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26470     
26471     this.addEvents({
26472         /**
26473          * @event beforeselectfile
26474          * Fire before select file
26475          * @param {Roo.bootstrap.UploadCropbox} this
26476          */
26477         "beforeselectfile" : true,
26478         /**
26479          * @event initial
26480          * Fire after initEvent
26481          * @param {Roo.bootstrap.UploadCropbox} this
26482          */
26483         "initial" : true,
26484         /**
26485          * @event crop
26486          * Fire after initEvent
26487          * @param {Roo.bootstrap.UploadCropbox} this
26488          * @param {String} data
26489          */
26490         "crop" : true,
26491         /**
26492          * @event prepare
26493          * Fire when preparing the file data
26494          * @param {Roo.bootstrap.UploadCropbox} this
26495          * @param {Object} file
26496          */
26497         "prepare" : true,
26498         /**
26499          * @event exception
26500          * Fire when get exception
26501          * @param {Roo.bootstrap.UploadCropbox} this
26502          * @param {XMLHttpRequest} xhr
26503          */
26504         "exception" : true,
26505         /**
26506          * @event beforeloadcanvas
26507          * Fire before load the canvas
26508          * @param {Roo.bootstrap.UploadCropbox} this
26509          * @param {String} src
26510          */
26511         "beforeloadcanvas" : true,
26512         /**
26513          * @event trash
26514          * Fire when trash image
26515          * @param {Roo.bootstrap.UploadCropbox} this
26516          */
26517         "trash" : true,
26518         /**
26519          * @event download
26520          * Fire when download the image
26521          * @param {Roo.bootstrap.UploadCropbox} this
26522          */
26523         "download" : true,
26524         /**
26525          * @event footerbuttonclick
26526          * Fire when footerbuttonclick
26527          * @param {Roo.bootstrap.UploadCropbox} this
26528          * @param {String} type
26529          */
26530         "footerbuttonclick" : true,
26531         /**
26532          * @event resize
26533          * Fire when resize
26534          * @param {Roo.bootstrap.UploadCropbox} this
26535          */
26536         "resize" : true,
26537         /**
26538          * @event rotate
26539          * Fire when rotate the image
26540          * @param {Roo.bootstrap.UploadCropbox} this
26541          * @param {String} pos
26542          */
26543         "rotate" : true,
26544         /**
26545          * @event inspect
26546          * Fire when inspect the file
26547          * @param {Roo.bootstrap.UploadCropbox} this
26548          * @param {Object} file
26549          */
26550         "inspect" : true,
26551         /**
26552          * @event upload
26553          * Fire when xhr upload the file
26554          * @param {Roo.bootstrap.UploadCropbox} this
26555          * @param {Object} data
26556          */
26557         "upload" : true,
26558         /**
26559          * @event arrange
26560          * Fire when arrange the file data
26561          * @param {Roo.bootstrap.UploadCropbox} this
26562          * @param {Object} formData
26563          */
26564         "arrange" : true
26565     });
26566     
26567     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26568 };
26569
26570 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26571     
26572     emptyText : 'Click to upload image',
26573     rotateNotify : 'Image is too small to rotate',
26574     errorTimeout : 3000,
26575     scale : 0,
26576     baseScale : 1,
26577     rotate : 0,
26578     dragable : false,
26579     pinching : false,
26580     mouseX : 0,
26581     mouseY : 0,
26582     cropData : false,
26583     minWidth : 300,
26584     minHeight : 300,
26585     file : false,
26586     exif : {},
26587     baseRotate : 1,
26588     cropType : 'image/jpeg',
26589     buttons : false,
26590     canvasLoaded : false,
26591     isDocument : false,
26592     method : 'POST',
26593     paramName : 'imageUpload',
26594     loadMask : true,
26595     loadingText : 'Loading...',
26596     maskEl : false,
26597     
26598     getAutoCreate : function()
26599     {
26600         var cfg = {
26601             tag : 'div',
26602             cls : 'roo-upload-cropbox',
26603             cn : [
26604                 {
26605                     tag : 'input',
26606                     cls : 'roo-upload-cropbox-selector',
26607                     type : 'file'
26608                 },
26609                 {
26610                     tag : 'div',
26611                     cls : 'roo-upload-cropbox-body',
26612                     style : 'cursor:pointer',
26613                     cn : [
26614                         {
26615                             tag : 'div',
26616                             cls : 'roo-upload-cropbox-preview'
26617                         },
26618                         {
26619                             tag : 'div',
26620                             cls : 'roo-upload-cropbox-thumb'
26621                         },
26622                         {
26623                             tag : 'div',
26624                             cls : 'roo-upload-cropbox-empty-notify',
26625                             html : this.emptyText
26626                         },
26627                         {
26628                             tag : 'div',
26629                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26630                             html : this.rotateNotify
26631                         }
26632                     ]
26633                 },
26634                 {
26635                     tag : 'div',
26636                     cls : 'roo-upload-cropbox-footer',
26637                     cn : {
26638                         tag : 'div',
26639                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26640                         cn : []
26641                     }
26642                 }
26643             ]
26644         };
26645         
26646         return cfg;
26647     },
26648     
26649     onRender : function(ct, position)
26650     {
26651         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26652         
26653         if (this.buttons.length) {
26654             
26655             Roo.each(this.buttons, function(bb) {
26656                 
26657                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26658                 
26659                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26660                 
26661             }, this);
26662         }
26663         
26664         if(this.loadMask){
26665             this.maskEl = this.el;
26666         }
26667     },
26668     
26669     initEvents : function()
26670     {
26671         this.urlAPI = (window.createObjectURL && window) || 
26672                                 (window.URL && URL.revokeObjectURL && URL) || 
26673                                 (window.webkitURL && webkitURL);
26674                         
26675         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26676         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26677         
26678         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26679         this.selectorEl.hide();
26680         
26681         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26682         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26683         
26684         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26685         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26686         this.thumbEl.hide();
26687         
26688         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26689         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26690         
26691         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26692         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26693         this.errorEl.hide();
26694         
26695         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26696         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26697         this.footerEl.hide();
26698         
26699         this.setThumbBoxSize();
26700         
26701         this.bind();
26702         
26703         this.resize();
26704         
26705         this.fireEvent('initial', this);
26706     },
26707
26708     bind : function()
26709     {
26710         var _this = this;
26711         
26712         window.addEventListener("resize", function() { _this.resize(); } );
26713         
26714         this.bodyEl.on('click', this.beforeSelectFile, this);
26715         
26716         if(Roo.isTouch){
26717             this.bodyEl.on('touchstart', this.onTouchStart, this);
26718             this.bodyEl.on('touchmove', this.onTouchMove, this);
26719             this.bodyEl.on('touchend', this.onTouchEnd, this);
26720         }
26721         
26722         if(!Roo.isTouch){
26723             this.bodyEl.on('mousedown', this.onMouseDown, this);
26724             this.bodyEl.on('mousemove', this.onMouseMove, this);
26725             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26726             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26727             Roo.get(document).on('mouseup', this.onMouseUp, this);
26728         }
26729         
26730         this.selectorEl.on('change', this.onFileSelected, this);
26731     },
26732     
26733     reset : function()
26734     {    
26735         this.scale = 0;
26736         this.baseScale = 1;
26737         this.rotate = 0;
26738         this.baseRotate = 1;
26739         this.dragable = false;
26740         this.pinching = false;
26741         this.mouseX = 0;
26742         this.mouseY = 0;
26743         this.cropData = false;
26744         this.notifyEl.dom.innerHTML = this.emptyText;
26745         
26746         this.selectorEl.dom.value = '';
26747         
26748     },
26749     
26750     resize : function()
26751     {
26752         if(this.fireEvent('resize', this) != false){
26753             this.setThumbBoxPosition();
26754             this.setCanvasPosition();
26755         }
26756     },
26757     
26758     onFooterButtonClick : function(e, el, o, type)
26759     {
26760         switch (type) {
26761             case 'rotate-left' :
26762                 this.onRotateLeft(e);
26763                 break;
26764             case 'rotate-right' :
26765                 this.onRotateRight(e);
26766                 break;
26767             case 'picture' :
26768                 this.beforeSelectFile(e);
26769                 break;
26770             case 'trash' :
26771                 this.trash(e);
26772                 break;
26773             case 'crop' :
26774                 this.crop(e);
26775                 break;
26776             case 'download' :
26777                 this.download(e);
26778                 break;
26779             default :
26780                 break;
26781         }
26782         
26783         this.fireEvent('footerbuttonclick', this, type);
26784     },
26785     
26786     beforeSelectFile : function(e)
26787     {
26788         e.preventDefault();
26789         
26790         if(this.fireEvent('beforeselectfile', this) != false){
26791             this.selectorEl.dom.click();
26792         }
26793     },
26794     
26795     onFileSelected : function(e)
26796     {
26797         e.preventDefault();
26798         
26799         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26800             return;
26801         }
26802         
26803         var file = this.selectorEl.dom.files[0];
26804         
26805         if(this.fireEvent('inspect', this, file) != false){
26806             this.prepare(file);
26807         }
26808         
26809     },
26810     
26811     trash : function(e)
26812     {
26813         this.fireEvent('trash', this);
26814     },
26815     
26816     download : function(e)
26817     {
26818         this.fireEvent('download', this);
26819     },
26820     
26821     loadCanvas : function(src)
26822     {   
26823         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26824             
26825             this.reset();
26826             
26827             this.imageEl = document.createElement('img');
26828             
26829             var _this = this;
26830             
26831             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26832             
26833             this.imageEl.src = src;
26834         }
26835     },
26836     
26837     onLoadCanvas : function()
26838     {   
26839         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26840         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26841         
26842         this.bodyEl.un('click', this.beforeSelectFile, this);
26843         
26844         this.notifyEl.hide();
26845         this.thumbEl.show();
26846         this.footerEl.show();
26847         
26848         this.baseRotateLevel();
26849         
26850         if(this.isDocument){
26851             this.setThumbBoxSize();
26852         }
26853         
26854         this.setThumbBoxPosition();
26855         
26856         this.baseScaleLevel();
26857         
26858         this.draw();
26859         
26860         this.resize();
26861         
26862         this.canvasLoaded = true;
26863         
26864         if(this.loadMask){
26865             this.maskEl.unmask();
26866         }
26867         
26868     },
26869     
26870     setCanvasPosition : function()
26871     {   
26872         if(!this.canvasEl){
26873             return;
26874         }
26875         
26876         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26877         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26878         
26879         this.previewEl.setLeft(pw);
26880         this.previewEl.setTop(ph);
26881         
26882     },
26883     
26884     onMouseDown : function(e)
26885     {   
26886         e.stopEvent();
26887         
26888         this.dragable = true;
26889         this.pinching = false;
26890         
26891         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26892             this.dragable = false;
26893             return;
26894         }
26895         
26896         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26897         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26898         
26899     },
26900     
26901     onMouseMove : function(e)
26902     {   
26903         e.stopEvent();
26904         
26905         if(!this.canvasLoaded){
26906             return;
26907         }
26908         
26909         if (!this.dragable){
26910             return;
26911         }
26912         
26913         var minX = Math.ceil(this.thumbEl.getLeft(true));
26914         var minY = Math.ceil(this.thumbEl.getTop(true));
26915         
26916         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26917         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26918         
26919         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26920         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26921         
26922         x = x - this.mouseX;
26923         y = y - this.mouseY;
26924         
26925         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26926         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26927         
26928         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26929         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26930         
26931         this.previewEl.setLeft(bgX);
26932         this.previewEl.setTop(bgY);
26933         
26934         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26935         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26936     },
26937     
26938     onMouseUp : function(e)
26939     {   
26940         e.stopEvent();
26941         
26942         this.dragable = false;
26943     },
26944     
26945     onMouseWheel : function(e)
26946     {   
26947         e.stopEvent();
26948         
26949         this.startScale = this.scale;
26950         
26951         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26952         
26953         if(!this.zoomable()){
26954             this.scale = this.startScale;
26955             return;
26956         }
26957         
26958         this.draw();
26959         
26960         return;
26961     },
26962     
26963     zoomable : function()
26964     {
26965         var minScale = this.thumbEl.getWidth() / this.minWidth;
26966         
26967         if(this.minWidth < this.minHeight){
26968             minScale = this.thumbEl.getHeight() / this.minHeight;
26969         }
26970         
26971         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26972         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26973         
26974         if(
26975                 this.isDocument &&
26976                 (this.rotate == 0 || this.rotate == 180) && 
26977                 (
26978                     width > this.imageEl.OriginWidth || 
26979                     height > this.imageEl.OriginHeight ||
26980                     (width < this.minWidth && height < this.minHeight)
26981                 )
26982         ){
26983             return false;
26984         }
26985         
26986         if(
26987                 this.isDocument &&
26988                 (this.rotate == 90 || this.rotate == 270) && 
26989                 (
26990                     width > this.imageEl.OriginWidth || 
26991                     height > this.imageEl.OriginHeight ||
26992                     (width < this.minHeight && height < this.minWidth)
26993                 )
26994         ){
26995             return false;
26996         }
26997         
26998         if(
26999                 !this.isDocument &&
27000                 (this.rotate == 0 || this.rotate == 180) && 
27001                 (
27002                     width < this.minWidth || 
27003                     width > this.imageEl.OriginWidth || 
27004                     height < this.minHeight || 
27005                     height > this.imageEl.OriginHeight
27006                 )
27007         ){
27008             return false;
27009         }
27010         
27011         if(
27012                 !this.isDocument &&
27013                 (this.rotate == 90 || this.rotate == 270) && 
27014                 (
27015                     width < this.minHeight || 
27016                     width > this.imageEl.OriginWidth || 
27017                     height < this.minWidth || 
27018                     height > this.imageEl.OriginHeight
27019                 )
27020         ){
27021             return false;
27022         }
27023         
27024         return true;
27025         
27026     },
27027     
27028     onRotateLeft : function(e)
27029     {   
27030         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27031             
27032             var minScale = this.thumbEl.getWidth() / this.minWidth;
27033             
27034             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27035             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27036             
27037             this.startScale = this.scale;
27038             
27039             while (this.getScaleLevel() < minScale){
27040             
27041                 this.scale = this.scale + 1;
27042                 
27043                 if(!this.zoomable()){
27044                     break;
27045                 }
27046                 
27047                 if(
27048                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27049                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27050                 ){
27051                     continue;
27052                 }
27053                 
27054                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27055
27056                 this.draw();
27057                 
27058                 return;
27059             }
27060             
27061             this.scale = this.startScale;
27062             
27063             this.onRotateFail();
27064             
27065             return false;
27066         }
27067         
27068         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27069
27070         if(this.isDocument){
27071             this.setThumbBoxSize();
27072             this.setThumbBoxPosition();
27073             this.setCanvasPosition();
27074         }
27075         
27076         this.draw();
27077         
27078         this.fireEvent('rotate', this, 'left');
27079         
27080     },
27081     
27082     onRotateRight : function(e)
27083     {
27084         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27085             
27086             var minScale = this.thumbEl.getWidth() / this.minWidth;
27087         
27088             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27089             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27090             
27091             this.startScale = this.scale;
27092             
27093             while (this.getScaleLevel() < minScale){
27094             
27095                 this.scale = this.scale + 1;
27096                 
27097                 if(!this.zoomable()){
27098                     break;
27099                 }
27100                 
27101                 if(
27102                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27103                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27104                 ){
27105                     continue;
27106                 }
27107                 
27108                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27109
27110                 this.draw();
27111                 
27112                 return;
27113             }
27114             
27115             this.scale = this.startScale;
27116             
27117             this.onRotateFail();
27118             
27119             return false;
27120         }
27121         
27122         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27123
27124         if(this.isDocument){
27125             this.setThumbBoxSize();
27126             this.setThumbBoxPosition();
27127             this.setCanvasPosition();
27128         }
27129         
27130         this.draw();
27131         
27132         this.fireEvent('rotate', this, 'right');
27133     },
27134     
27135     onRotateFail : function()
27136     {
27137         this.errorEl.show(true);
27138         
27139         var _this = this;
27140         
27141         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27142     },
27143     
27144     draw : function()
27145     {
27146         this.previewEl.dom.innerHTML = '';
27147         
27148         var canvasEl = document.createElement("canvas");
27149         
27150         var contextEl = canvasEl.getContext("2d");
27151         
27152         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27153         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27154         var center = this.imageEl.OriginWidth / 2;
27155         
27156         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27157             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27158             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27159             center = this.imageEl.OriginHeight / 2;
27160         }
27161         
27162         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27163         
27164         contextEl.translate(center, center);
27165         contextEl.rotate(this.rotate * Math.PI / 180);
27166
27167         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27168         
27169         this.canvasEl = document.createElement("canvas");
27170         
27171         this.contextEl = this.canvasEl.getContext("2d");
27172         
27173         switch (this.rotate) {
27174             case 0 :
27175                 
27176                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27177                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27178                 
27179                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27180                 
27181                 break;
27182             case 90 : 
27183                 
27184                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27185                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27186                 
27187                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27188                     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);
27189                     break;
27190                 }
27191                 
27192                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27193                 
27194                 break;
27195             case 180 :
27196                 
27197                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27198                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27199                 
27200                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27201                     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);
27202                     break;
27203                 }
27204                 
27205                 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);
27206                 
27207                 break;
27208             case 270 :
27209                 
27210                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27211                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27212         
27213                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27214                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27215                     break;
27216                 }
27217                 
27218                 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);
27219                 
27220                 break;
27221             default : 
27222                 break;
27223         }
27224         
27225         this.previewEl.appendChild(this.canvasEl);
27226         
27227         this.setCanvasPosition();
27228     },
27229     
27230     crop : function()
27231     {
27232         if(!this.canvasLoaded){
27233             return;
27234         }
27235         
27236         var imageCanvas = document.createElement("canvas");
27237         
27238         var imageContext = imageCanvas.getContext("2d");
27239         
27240         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27241         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27242         
27243         var center = imageCanvas.width / 2;
27244         
27245         imageContext.translate(center, center);
27246         
27247         imageContext.rotate(this.rotate * Math.PI / 180);
27248         
27249         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27250         
27251         var canvas = document.createElement("canvas");
27252         
27253         var context = canvas.getContext("2d");
27254                 
27255         canvas.width = this.minWidth;
27256         canvas.height = this.minHeight;
27257
27258         switch (this.rotate) {
27259             case 0 :
27260                 
27261                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27262                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27263                 
27264                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27265                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27266                 
27267                 var targetWidth = this.minWidth - 2 * x;
27268                 var targetHeight = this.minHeight - 2 * y;
27269                 
27270                 var scale = 1;
27271                 
27272                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27273                     scale = targetWidth / width;
27274                 }
27275                 
27276                 if(x > 0 && y == 0){
27277                     scale = targetHeight / height;
27278                 }
27279                 
27280                 if(x > 0 && y > 0){
27281                     scale = targetWidth / width;
27282                     
27283                     if(width < height){
27284                         scale = targetHeight / height;
27285                     }
27286                 }
27287                 
27288                 context.scale(scale, scale);
27289                 
27290                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27291                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27292
27293                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27294                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27295
27296                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27297                 
27298                 break;
27299             case 90 : 
27300                 
27301                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27302                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27303                 
27304                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27305                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27306                 
27307                 var targetWidth = this.minWidth - 2 * x;
27308                 var targetHeight = this.minHeight - 2 * y;
27309                 
27310                 var scale = 1;
27311                 
27312                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27313                     scale = targetWidth / width;
27314                 }
27315                 
27316                 if(x > 0 && y == 0){
27317                     scale = targetHeight / height;
27318                 }
27319                 
27320                 if(x > 0 && y > 0){
27321                     scale = targetWidth / width;
27322                     
27323                     if(width < height){
27324                         scale = targetHeight / height;
27325                     }
27326                 }
27327                 
27328                 context.scale(scale, scale);
27329                 
27330                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27331                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27332
27333                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27334                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27335                 
27336                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27337                 
27338                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27339                 
27340                 break;
27341             case 180 :
27342                 
27343                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27344                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27345                 
27346                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27347                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27348                 
27349                 var targetWidth = this.minWidth - 2 * x;
27350                 var targetHeight = this.minHeight - 2 * y;
27351                 
27352                 var scale = 1;
27353                 
27354                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27355                     scale = targetWidth / width;
27356                 }
27357                 
27358                 if(x > 0 && y == 0){
27359                     scale = targetHeight / height;
27360                 }
27361                 
27362                 if(x > 0 && y > 0){
27363                     scale = targetWidth / width;
27364                     
27365                     if(width < height){
27366                         scale = targetHeight / height;
27367                     }
27368                 }
27369                 
27370                 context.scale(scale, scale);
27371                 
27372                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27373                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27374
27375                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27376                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27377
27378                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27379                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27380                 
27381                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27382                 
27383                 break;
27384             case 270 :
27385                 
27386                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27387                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27388                 
27389                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27390                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27391                 
27392                 var targetWidth = this.minWidth - 2 * x;
27393                 var targetHeight = this.minHeight - 2 * y;
27394                 
27395                 var scale = 1;
27396                 
27397                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27398                     scale = targetWidth / width;
27399                 }
27400                 
27401                 if(x > 0 && y == 0){
27402                     scale = targetHeight / height;
27403                 }
27404                 
27405                 if(x > 0 && y > 0){
27406                     scale = targetWidth / width;
27407                     
27408                     if(width < height){
27409                         scale = targetHeight / height;
27410                     }
27411                 }
27412                 
27413                 context.scale(scale, scale);
27414                 
27415                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27416                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27417
27418                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27419                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27420                 
27421                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27422                 
27423                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27424                 
27425                 break;
27426             default : 
27427                 break;
27428         }
27429         
27430         this.cropData = canvas.toDataURL(this.cropType);
27431         
27432         if(this.fireEvent('crop', this, this.cropData) !== false){
27433             this.process(this.file, this.cropData);
27434         }
27435         
27436         return;
27437         
27438     },
27439     
27440     setThumbBoxSize : function()
27441     {
27442         var width, height;
27443         
27444         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27445             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27446             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27447             
27448             this.minWidth = width;
27449             this.minHeight = height;
27450             
27451             if(this.rotate == 90 || this.rotate == 270){
27452                 this.minWidth = height;
27453                 this.minHeight = width;
27454             }
27455         }
27456         
27457         height = 300;
27458         width = Math.ceil(this.minWidth * height / this.minHeight);
27459         
27460         if(this.minWidth > this.minHeight){
27461             width = 300;
27462             height = Math.ceil(this.minHeight * width / this.minWidth);
27463         }
27464         
27465         this.thumbEl.setStyle({
27466             width : width + 'px',
27467             height : height + 'px'
27468         });
27469
27470         return;
27471             
27472     },
27473     
27474     setThumbBoxPosition : function()
27475     {
27476         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27477         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27478         
27479         this.thumbEl.setLeft(x);
27480         this.thumbEl.setTop(y);
27481         
27482     },
27483     
27484     baseRotateLevel : function()
27485     {
27486         this.baseRotate = 1;
27487         
27488         if(
27489                 typeof(this.exif) != 'undefined' &&
27490                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27491                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27492         ){
27493             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27494         }
27495         
27496         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27497         
27498     },
27499     
27500     baseScaleLevel : function()
27501     {
27502         var width, height;
27503         
27504         if(this.isDocument){
27505             
27506             if(this.baseRotate == 6 || this.baseRotate == 8){
27507             
27508                 height = this.thumbEl.getHeight();
27509                 this.baseScale = height / this.imageEl.OriginWidth;
27510
27511                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27512                     width = this.thumbEl.getWidth();
27513                     this.baseScale = width / this.imageEl.OriginHeight;
27514                 }
27515
27516                 return;
27517             }
27518
27519             height = this.thumbEl.getHeight();
27520             this.baseScale = height / this.imageEl.OriginHeight;
27521
27522             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27523                 width = this.thumbEl.getWidth();
27524                 this.baseScale = width / this.imageEl.OriginWidth;
27525             }
27526
27527             return;
27528         }
27529         
27530         if(this.baseRotate == 6 || this.baseRotate == 8){
27531             
27532             width = this.thumbEl.getHeight();
27533             this.baseScale = width / this.imageEl.OriginHeight;
27534             
27535             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27536                 height = this.thumbEl.getWidth();
27537                 this.baseScale = height / this.imageEl.OriginHeight;
27538             }
27539             
27540             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27541                 height = this.thumbEl.getWidth();
27542                 this.baseScale = height / this.imageEl.OriginHeight;
27543                 
27544                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27545                     width = this.thumbEl.getHeight();
27546                     this.baseScale = width / this.imageEl.OriginWidth;
27547                 }
27548             }
27549             
27550             return;
27551         }
27552         
27553         width = this.thumbEl.getWidth();
27554         this.baseScale = width / this.imageEl.OriginWidth;
27555         
27556         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27557             height = this.thumbEl.getHeight();
27558             this.baseScale = height / this.imageEl.OriginHeight;
27559         }
27560         
27561         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27562             
27563             height = this.thumbEl.getHeight();
27564             this.baseScale = height / this.imageEl.OriginHeight;
27565             
27566             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27567                 width = this.thumbEl.getWidth();
27568                 this.baseScale = width / this.imageEl.OriginWidth;
27569             }
27570             
27571         }
27572         
27573         return;
27574     },
27575     
27576     getScaleLevel : function()
27577     {
27578         return this.baseScale * Math.pow(1.1, this.scale);
27579     },
27580     
27581     onTouchStart : function(e)
27582     {
27583         if(!this.canvasLoaded){
27584             this.beforeSelectFile(e);
27585             return;
27586         }
27587         
27588         var touches = e.browserEvent.touches;
27589         
27590         if(!touches){
27591             return;
27592         }
27593         
27594         if(touches.length == 1){
27595             this.onMouseDown(e);
27596             return;
27597         }
27598         
27599         if(touches.length != 2){
27600             return;
27601         }
27602         
27603         var coords = [];
27604         
27605         for(var i = 0, finger; finger = touches[i]; i++){
27606             coords.push(finger.pageX, finger.pageY);
27607         }
27608         
27609         var x = Math.pow(coords[0] - coords[2], 2);
27610         var y = Math.pow(coords[1] - coords[3], 2);
27611         
27612         this.startDistance = Math.sqrt(x + y);
27613         
27614         this.startScale = this.scale;
27615         
27616         this.pinching = true;
27617         this.dragable = false;
27618         
27619     },
27620     
27621     onTouchMove : function(e)
27622     {
27623         if(!this.pinching && !this.dragable){
27624             return;
27625         }
27626         
27627         var touches = e.browserEvent.touches;
27628         
27629         if(!touches){
27630             return;
27631         }
27632         
27633         if(this.dragable){
27634             this.onMouseMove(e);
27635             return;
27636         }
27637         
27638         var coords = [];
27639         
27640         for(var i = 0, finger; finger = touches[i]; i++){
27641             coords.push(finger.pageX, finger.pageY);
27642         }
27643         
27644         var x = Math.pow(coords[0] - coords[2], 2);
27645         var y = Math.pow(coords[1] - coords[3], 2);
27646         
27647         this.endDistance = Math.sqrt(x + y);
27648         
27649         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27650         
27651         if(!this.zoomable()){
27652             this.scale = this.startScale;
27653             return;
27654         }
27655         
27656         this.draw();
27657         
27658     },
27659     
27660     onTouchEnd : function(e)
27661     {
27662         this.pinching = false;
27663         this.dragable = false;
27664         
27665     },
27666     
27667     process : function(file, crop)
27668     {
27669         if(this.loadMask){
27670             this.maskEl.mask(this.loadingText);
27671         }
27672         
27673         this.xhr = new XMLHttpRequest();
27674         
27675         file.xhr = this.xhr;
27676
27677         this.xhr.open(this.method, this.url, true);
27678         
27679         var headers = {
27680             "Accept": "application/json",
27681             "Cache-Control": "no-cache",
27682             "X-Requested-With": "XMLHttpRequest"
27683         };
27684         
27685         for (var headerName in headers) {
27686             var headerValue = headers[headerName];
27687             if (headerValue) {
27688                 this.xhr.setRequestHeader(headerName, headerValue);
27689             }
27690         }
27691         
27692         var _this = this;
27693         
27694         this.xhr.onload = function()
27695         {
27696             _this.xhrOnLoad(_this.xhr);
27697         }
27698         
27699         this.xhr.onerror = function()
27700         {
27701             _this.xhrOnError(_this.xhr);
27702         }
27703         
27704         var formData = new FormData();
27705
27706         formData.append('returnHTML', 'NO');
27707         
27708         if(crop){
27709             formData.append('crop', crop);
27710         }
27711         
27712         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27713             formData.append(this.paramName, file, file.name);
27714         }
27715         
27716         if(typeof(file.filename) != 'undefined'){
27717             formData.append('filename', file.filename);
27718         }
27719         
27720         if(typeof(file.mimetype) != 'undefined'){
27721             formData.append('mimetype', file.mimetype);
27722         }
27723         
27724         if(this.fireEvent('arrange', this, formData) != false){
27725             this.xhr.send(formData);
27726         };
27727     },
27728     
27729     xhrOnLoad : function(xhr)
27730     {
27731         if(this.loadMask){
27732             this.maskEl.unmask();
27733         }
27734         
27735         if (xhr.readyState !== 4) {
27736             this.fireEvent('exception', this, xhr);
27737             return;
27738         }
27739
27740         var response = Roo.decode(xhr.responseText);
27741         
27742         if(!response.success){
27743             this.fireEvent('exception', this, xhr);
27744             return;
27745         }
27746         
27747         var response = Roo.decode(xhr.responseText);
27748         
27749         this.fireEvent('upload', this, response);
27750         
27751     },
27752     
27753     xhrOnError : function()
27754     {
27755         if(this.loadMask){
27756             this.maskEl.unmask();
27757         }
27758         
27759         Roo.log('xhr on error');
27760         
27761         var response = Roo.decode(xhr.responseText);
27762           
27763         Roo.log(response);
27764         
27765     },
27766     
27767     prepare : function(file)
27768     {   
27769         if(this.loadMask){
27770             this.maskEl.mask(this.loadingText);
27771         }
27772         
27773         this.file = false;
27774         this.exif = {};
27775         
27776         if(typeof(file) === 'string'){
27777             this.loadCanvas(file);
27778             return;
27779         }
27780         
27781         if(!file || !this.urlAPI){
27782             return;
27783         }
27784         
27785         this.file = file;
27786         this.cropType = file.type;
27787         
27788         var _this = this;
27789         
27790         if(this.fireEvent('prepare', this, this.file) != false){
27791             
27792             var reader = new FileReader();
27793             
27794             reader.onload = function (e) {
27795                 if (e.target.error) {
27796                     Roo.log(e.target.error);
27797                     return;
27798                 }
27799                 
27800                 var buffer = e.target.result,
27801                     dataView = new DataView(buffer),
27802                     offset = 2,
27803                     maxOffset = dataView.byteLength - 4,
27804                     markerBytes,
27805                     markerLength;
27806                 
27807                 if (dataView.getUint16(0) === 0xffd8) {
27808                     while (offset < maxOffset) {
27809                         markerBytes = dataView.getUint16(offset);
27810                         
27811                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27812                             markerLength = dataView.getUint16(offset + 2) + 2;
27813                             if (offset + markerLength > dataView.byteLength) {
27814                                 Roo.log('Invalid meta data: Invalid segment size.');
27815                                 break;
27816                             }
27817                             
27818                             if(markerBytes == 0xffe1){
27819                                 _this.parseExifData(
27820                                     dataView,
27821                                     offset,
27822                                     markerLength
27823                                 );
27824                             }
27825                             
27826                             offset += markerLength;
27827                             
27828                             continue;
27829                         }
27830                         
27831                         break;
27832                     }
27833                     
27834                 }
27835                 
27836                 var url = _this.urlAPI.createObjectURL(_this.file);
27837                 
27838                 _this.loadCanvas(url);
27839                 
27840                 return;
27841             }
27842             
27843             reader.readAsArrayBuffer(this.file);
27844             
27845         }
27846         
27847     },
27848     
27849     parseExifData : function(dataView, offset, length)
27850     {
27851         var tiffOffset = offset + 10,
27852             littleEndian,
27853             dirOffset;
27854     
27855         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27856             // No Exif data, might be XMP data instead
27857             return;
27858         }
27859         
27860         // Check for the ASCII code for "Exif" (0x45786966):
27861         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27862             // No Exif data, might be XMP data instead
27863             return;
27864         }
27865         if (tiffOffset + 8 > dataView.byteLength) {
27866             Roo.log('Invalid Exif data: Invalid segment size.');
27867             return;
27868         }
27869         // Check for the two null bytes:
27870         if (dataView.getUint16(offset + 8) !== 0x0000) {
27871             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27872             return;
27873         }
27874         // Check the byte alignment:
27875         switch (dataView.getUint16(tiffOffset)) {
27876         case 0x4949:
27877             littleEndian = true;
27878             break;
27879         case 0x4D4D:
27880             littleEndian = false;
27881             break;
27882         default:
27883             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27884             return;
27885         }
27886         // Check for the TIFF tag marker (0x002A):
27887         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27888             Roo.log('Invalid Exif data: Missing TIFF marker.');
27889             return;
27890         }
27891         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27892         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27893         
27894         this.parseExifTags(
27895             dataView,
27896             tiffOffset,
27897             tiffOffset + dirOffset,
27898             littleEndian
27899         );
27900     },
27901     
27902     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27903     {
27904         var tagsNumber,
27905             dirEndOffset,
27906             i;
27907         if (dirOffset + 6 > dataView.byteLength) {
27908             Roo.log('Invalid Exif data: Invalid directory offset.');
27909             return;
27910         }
27911         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27912         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27913         if (dirEndOffset + 4 > dataView.byteLength) {
27914             Roo.log('Invalid Exif data: Invalid directory size.');
27915             return;
27916         }
27917         for (i = 0; i < tagsNumber; i += 1) {
27918             this.parseExifTag(
27919                 dataView,
27920                 tiffOffset,
27921                 dirOffset + 2 + 12 * i, // tag offset
27922                 littleEndian
27923             );
27924         }
27925         // Return the offset to the next directory:
27926         return dataView.getUint32(dirEndOffset, littleEndian);
27927     },
27928     
27929     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27930     {
27931         var tag = dataView.getUint16(offset, littleEndian);
27932         
27933         this.exif[tag] = this.getExifValue(
27934             dataView,
27935             tiffOffset,
27936             offset,
27937             dataView.getUint16(offset + 2, littleEndian), // tag type
27938             dataView.getUint32(offset + 4, littleEndian), // tag length
27939             littleEndian
27940         );
27941     },
27942     
27943     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27944     {
27945         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27946             tagSize,
27947             dataOffset,
27948             values,
27949             i,
27950             str,
27951             c;
27952     
27953         if (!tagType) {
27954             Roo.log('Invalid Exif data: Invalid tag type.');
27955             return;
27956         }
27957         
27958         tagSize = tagType.size * length;
27959         // Determine if the value is contained in the dataOffset bytes,
27960         // or if the value at the dataOffset is a pointer to the actual data:
27961         dataOffset = tagSize > 4 ?
27962                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27963         if (dataOffset + tagSize > dataView.byteLength) {
27964             Roo.log('Invalid Exif data: Invalid data offset.');
27965             return;
27966         }
27967         if (length === 1) {
27968             return tagType.getValue(dataView, dataOffset, littleEndian);
27969         }
27970         values = [];
27971         for (i = 0; i < length; i += 1) {
27972             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27973         }
27974         
27975         if (tagType.ascii) {
27976             str = '';
27977             // Concatenate the chars:
27978             for (i = 0; i < values.length; i += 1) {
27979                 c = values[i];
27980                 // Ignore the terminating NULL byte(s):
27981                 if (c === '\u0000') {
27982                     break;
27983                 }
27984                 str += c;
27985             }
27986             return str;
27987         }
27988         return values;
27989     }
27990     
27991 });
27992
27993 Roo.apply(Roo.bootstrap.UploadCropbox, {
27994     tags : {
27995         'Orientation': 0x0112
27996     },
27997     
27998     Orientation: {
27999             1: 0, //'top-left',
28000 //            2: 'top-right',
28001             3: 180, //'bottom-right',
28002 //            4: 'bottom-left',
28003 //            5: 'left-top',
28004             6: 90, //'right-top',
28005 //            7: 'right-bottom',
28006             8: 270 //'left-bottom'
28007     },
28008     
28009     exifTagTypes : {
28010         // byte, 8-bit unsigned int:
28011         1: {
28012             getValue: function (dataView, dataOffset) {
28013                 return dataView.getUint8(dataOffset);
28014             },
28015             size: 1
28016         },
28017         // ascii, 8-bit byte:
28018         2: {
28019             getValue: function (dataView, dataOffset) {
28020                 return String.fromCharCode(dataView.getUint8(dataOffset));
28021             },
28022             size: 1,
28023             ascii: true
28024         },
28025         // short, 16 bit int:
28026         3: {
28027             getValue: function (dataView, dataOffset, littleEndian) {
28028                 return dataView.getUint16(dataOffset, littleEndian);
28029             },
28030             size: 2
28031         },
28032         // long, 32 bit int:
28033         4: {
28034             getValue: function (dataView, dataOffset, littleEndian) {
28035                 return dataView.getUint32(dataOffset, littleEndian);
28036             },
28037             size: 4
28038         },
28039         // rational = two long values, first is numerator, second is denominator:
28040         5: {
28041             getValue: function (dataView, dataOffset, littleEndian) {
28042                 return dataView.getUint32(dataOffset, littleEndian) /
28043                     dataView.getUint32(dataOffset + 4, littleEndian);
28044             },
28045             size: 8
28046         },
28047         // slong, 32 bit signed int:
28048         9: {
28049             getValue: function (dataView, dataOffset, littleEndian) {
28050                 return dataView.getInt32(dataOffset, littleEndian);
28051             },
28052             size: 4
28053         },
28054         // srational, two slongs, first is numerator, second is denominator:
28055         10: {
28056             getValue: function (dataView, dataOffset, littleEndian) {
28057                 return dataView.getInt32(dataOffset, littleEndian) /
28058                     dataView.getInt32(dataOffset + 4, littleEndian);
28059             },
28060             size: 8
28061         }
28062     },
28063     
28064     footer : {
28065         STANDARD : [
28066             {
28067                 tag : 'div',
28068                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28069                 action : 'rotate-left',
28070                 cn : [
28071                     {
28072                         tag : 'button',
28073                         cls : 'btn btn-default',
28074                         html : '<i class="fa fa-undo"></i>'
28075                     }
28076                 ]
28077             },
28078             {
28079                 tag : 'div',
28080                 cls : 'btn-group roo-upload-cropbox-picture',
28081                 action : 'picture',
28082                 cn : [
28083                     {
28084                         tag : 'button',
28085                         cls : 'btn btn-default',
28086                         html : '<i class="fa fa-picture-o"></i>'
28087                     }
28088                 ]
28089             },
28090             {
28091                 tag : 'div',
28092                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28093                 action : 'rotate-right',
28094                 cn : [
28095                     {
28096                         tag : 'button',
28097                         cls : 'btn btn-default',
28098                         html : '<i class="fa fa-repeat"></i>'
28099                     }
28100                 ]
28101             }
28102         ],
28103         DOCUMENT : [
28104             {
28105                 tag : 'div',
28106                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28107                 action : 'rotate-left',
28108                 cn : [
28109                     {
28110                         tag : 'button',
28111                         cls : 'btn btn-default',
28112                         html : '<i class="fa fa-undo"></i>'
28113                     }
28114                 ]
28115             },
28116             {
28117                 tag : 'div',
28118                 cls : 'btn-group roo-upload-cropbox-download',
28119                 action : 'download',
28120                 cn : [
28121                     {
28122                         tag : 'button',
28123                         cls : 'btn btn-default',
28124                         html : '<i class="fa fa-download"></i>'
28125                     }
28126                 ]
28127             },
28128             {
28129                 tag : 'div',
28130                 cls : 'btn-group roo-upload-cropbox-crop',
28131                 action : 'crop',
28132                 cn : [
28133                     {
28134                         tag : 'button',
28135                         cls : 'btn btn-default',
28136                         html : '<i class="fa fa-crop"></i>'
28137                     }
28138                 ]
28139             },
28140             {
28141                 tag : 'div',
28142                 cls : 'btn-group roo-upload-cropbox-trash',
28143                 action : 'trash',
28144                 cn : [
28145                     {
28146                         tag : 'button',
28147                         cls : 'btn btn-default',
28148                         html : '<i class="fa fa-trash"></i>'
28149                     }
28150                 ]
28151             },
28152             {
28153                 tag : 'div',
28154                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28155                 action : 'rotate-right',
28156                 cn : [
28157                     {
28158                         tag : 'button',
28159                         cls : 'btn btn-default',
28160                         html : '<i class="fa fa-repeat"></i>'
28161                     }
28162                 ]
28163             }
28164         ],
28165         ROTATOR : [
28166             {
28167                 tag : 'div',
28168                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28169                 action : 'rotate-left',
28170                 cn : [
28171                     {
28172                         tag : 'button',
28173                         cls : 'btn btn-default',
28174                         html : '<i class="fa fa-undo"></i>'
28175                     }
28176                 ]
28177             },
28178             {
28179                 tag : 'div',
28180                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28181                 action : 'rotate-right',
28182                 cn : [
28183                     {
28184                         tag : 'button',
28185                         cls : 'btn btn-default',
28186                         html : '<i class="fa fa-repeat"></i>'
28187                     }
28188                 ]
28189             }
28190         ]
28191     }
28192 });
28193
28194 /*
28195 * Licence: LGPL
28196 */
28197
28198 /**
28199  * @class Roo.bootstrap.DocumentManager
28200  * @extends Roo.bootstrap.Component
28201  * Bootstrap DocumentManager class
28202  * @cfg {String} paramName default 'imageUpload'
28203  * @cfg {String} toolTipName default 'filename'
28204  * @cfg {String} method default POST
28205  * @cfg {String} url action url
28206  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28207  * @cfg {Boolean} multiple multiple upload default true
28208  * @cfg {Number} thumbSize default 300
28209  * @cfg {String} fieldLabel
28210  * @cfg {Number} labelWidth default 4
28211  * @cfg {String} labelAlign (left|top) default left
28212  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28213 * @cfg {Number} labellg set the width of label (1-12)
28214  * @cfg {Number} labelmd set the width of label (1-12)
28215  * @cfg {Number} labelsm set the width of label (1-12)
28216  * @cfg {Number} labelxs set the width of label (1-12)
28217  * 
28218  * @constructor
28219  * Create a new DocumentManager
28220  * @param {Object} config The config object
28221  */
28222
28223 Roo.bootstrap.DocumentManager = function(config){
28224     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28225     
28226     this.files = [];
28227     this.delegates = [];
28228     
28229     this.addEvents({
28230         /**
28231          * @event initial
28232          * Fire when initial the DocumentManager
28233          * @param {Roo.bootstrap.DocumentManager} this
28234          */
28235         "initial" : true,
28236         /**
28237          * @event inspect
28238          * inspect selected file
28239          * @param {Roo.bootstrap.DocumentManager} this
28240          * @param {File} file
28241          */
28242         "inspect" : true,
28243         /**
28244          * @event exception
28245          * Fire when xhr load exception
28246          * @param {Roo.bootstrap.DocumentManager} this
28247          * @param {XMLHttpRequest} xhr
28248          */
28249         "exception" : true,
28250         /**
28251          * @event afterupload
28252          * Fire when xhr load exception
28253          * @param {Roo.bootstrap.DocumentManager} this
28254          * @param {XMLHttpRequest} xhr
28255          */
28256         "afterupload" : true,
28257         /**
28258          * @event prepare
28259          * prepare the form data
28260          * @param {Roo.bootstrap.DocumentManager} this
28261          * @param {Object} formData
28262          */
28263         "prepare" : true,
28264         /**
28265          * @event remove
28266          * Fire when remove the file
28267          * @param {Roo.bootstrap.DocumentManager} this
28268          * @param {Object} file
28269          */
28270         "remove" : true,
28271         /**
28272          * @event refresh
28273          * Fire after refresh the file
28274          * @param {Roo.bootstrap.DocumentManager} this
28275          */
28276         "refresh" : true,
28277         /**
28278          * @event click
28279          * Fire after click the image
28280          * @param {Roo.bootstrap.DocumentManager} this
28281          * @param {Object} file
28282          */
28283         "click" : true,
28284         /**
28285          * @event edit
28286          * Fire when upload a image and editable set to true
28287          * @param {Roo.bootstrap.DocumentManager} this
28288          * @param {Object} file
28289          */
28290         "edit" : true,
28291         /**
28292          * @event beforeselectfile
28293          * Fire before select file
28294          * @param {Roo.bootstrap.DocumentManager} this
28295          */
28296         "beforeselectfile" : true,
28297         /**
28298          * @event process
28299          * Fire before process file
28300          * @param {Roo.bootstrap.DocumentManager} this
28301          * @param {Object} file
28302          */
28303         "process" : true
28304         
28305     });
28306 };
28307
28308 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28309     
28310     boxes : 0,
28311     inputName : '',
28312     thumbSize : 300,
28313     multiple : true,
28314     files : false,
28315     method : 'POST',
28316     url : '',
28317     paramName : 'imageUpload',
28318     toolTipName : 'filename',
28319     fieldLabel : '',
28320     labelWidth : 4,
28321     labelAlign : 'left',
28322     editable : true,
28323     delegates : false,
28324     xhr : false, 
28325     
28326     labellg : 0,
28327     labelmd : 0,
28328     labelsm : 0,
28329     labelxs : 0,
28330     
28331     getAutoCreate : function()
28332     {   
28333         var managerWidget = {
28334             tag : 'div',
28335             cls : 'roo-document-manager',
28336             cn : [
28337                 {
28338                     tag : 'input',
28339                     cls : 'roo-document-manager-selector',
28340                     type : 'file'
28341                 },
28342                 {
28343                     tag : 'div',
28344                     cls : 'roo-document-manager-uploader',
28345                     cn : [
28346                         {
28347                             tag : 'div',
28348                             cls : 'roo-document-manager-upload-btn',
28349                             html : '<i class="fa fa-plus"></i>'
28350                         }
28351                     ]
28352                     
28353                 }
28354             ]
28355         };
28356         
28357         var content = [
28358             {
28359                 tag : 'div',
28360                 cls : 'column col-md-12',
28361                 cn : managerWidget
28362             }
28363         ];
28364         
28365         if(this.fieldLabel.length){
28366             
28367             content = [
28368                 {
28369                     tag : 'div',
28370                     cls : 'column col-md-12',
28371                     html : this.fieldLabel
28372                 },
28373                 {
28374                     tag : 'div',
28375                     cls : 'column col-md-12',
28376                     cn : managerWidget
28377                 }
28378             ];
28379
28380             if(this.labelAlign == 'left'){
28381                 content = [
28382                     {
28383                         tag : 'div',
28384                         cls : 'column',
28385                         html : this.fieldLabel
28386                     },
28387                     {
28388                         tag : 'div',
28389                         cls : 'column',
28390                         cn : managerWidget
28391                     }
28392                 ];
28393                 
28394                 if(this.labelWidth > 12){
28395                     content[0].style = "width: " + this.labelWidth + 'px';
28396                 }
28397
28398                 if(this.labelWidth < 13 && this.labelmd == 0){
28399                     this.labelmd = this.labelWidth;
28400                 }
28401
28402                 if(this.labellg > 0){
28403                     content[0].cls += ' col-lg-' + this.labellg;
28404                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28405                 }
28406
28407                 if(this.labelmd > 0){
28408                     content[0].cls += ' col-md-' + this.labelmd;
28409                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28410                 }
28411
28412                 if(this.labelsm > 0){
28413                     content[0].cls += ' col-sm-' + this.labelsm;
28414                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28415                 }
28416
28417                 if(this.labelxs > 0){
28418                     content[0].cls += ' col-xs-' + this.labelxs;
28419                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28420                 }
28421                 
28422             }
28423         }
28424         
28425         var cfg = {
28426             tag : 'div',
28427             cls : 'row clearfix',
28428             cn : content
28429         };
28430         
28431         return cfg;
28432         
28433     },
28434     
28435     initEvents : function()
28436     {
28437         this.managerEl = this.el.select('.roo-document-manager', true).first();
28438         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28439         
28440         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28441         this.selectorEl.hide();
28442         
28443         if(this.multiple){
28444             this.selectorEl.attr('multiple', 'multiple');
28445         }
28446         
28447         this.selectorEl.on('change', this.onFileSelected, this);
28448         
28449         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28450         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28451         
28452         this.uploader.on('click', this.onUploaderClick, this);
28453         
28454         this.renderProgressDialog();
28455         
28456         var _this = this;
28457         
28458         window.addEventListener("resize", function() { _this.refresh(); } );
28459         
28460         this.fireEvent('initial', this);
28461     },
28462     
28463     renderProgressDialog : function()
28464     {
28465         var _this = this;
28466         
28467         this.progressDialog = new Roo.bootstrap.Modal({
28468             cls : 'roo-document-manager-progress-dialog',
28469             allow_close : false,
28470             title : '',
28471             buttons : [
28472                 {
28473                     name  :'cancel',
28474                     weight : 'danger',
28475                     html : 'Cancel'
28476                 }
28477             ], 
28478             listeners : { 
28479                 btnclick : function() {
28480                     _this.uploadCancel();
28481                     this.hide();
28482                 }
28483             }
28484         });
28485          
28486         this.progressDialog.render(Roo.get(document.body));
28487          
28488         this.progress = new Roo.bootstrap.Progress({
28489             cls : 'roo-document-manager-progress',
28490             active : true,
28491             striped : true
28492         });
28493         
28494         this.progress.render(this.progressDialog.getChildContainer());
28495         
28496         this.progressBar = new Roo.bootstrap.ProgressBar({
28497             cls : 'roo-document-manager-progress-bar',
28498             aria_valuenow : 0,
28499             aria_valuemin : 0,
28500             aria_valuemax : 12,
28501             panel : 'success'
28502         });
28503         
28504         this.progressBar.render(this.progress.getChildContainer());
28505     },
28506     
28507     onUploaderClick : function(e)
28508     {
28509         e.preventDefault();
28510      
28511         if(this.fireEvent('beforeselectfile', this) != false){
28512             this.selectorEl.dom.click();
28513         }
28514         
28515     },
28516     
28517     onFileSelected : function(e)
28518     {
28519         e.preventDefault();
28520         
28521         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28522             return;
28523         }
28524         
28525         Roo.each(this.selectorEl.dom.files, function(file){
28526             if(this.fireEvent('inspect', this, file) != false){
28527                 this.files.push(file);
28528             }
28529         }, this);
28530         
28531         this.queue();
28532         
28533     },
28534     
28535     queue : function()
28536     {
28537         this.selectorEl.dom.value = '';
28538         
28539         if(!this.files.length){
28540             return;
28541         }
28542         
28543         if(this.boxes > 0 && this.files.length > this.boxes){
28544             this.files = this.files.slice(0, this.boxes);
28545         }
28546         
28547         this.uploader.show();
28548         
28549         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28550             this.uploader.hide();
28551         }
28552         
28553         var _this = this;
28554         
28555         var files = [];
28556         
28557         var docs = [];
28558         
28559         Roo.each(this.files, function(file){
28560             
28561             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28562                 var f = this.renderPreview(file);
28563                 files.push(f);
28564                 return;
28565             }
28566             
28567             if(file.type.indexOf('image') != -1){
28568                 this.delegates.push(
28569                     (function(){
28570                         _this.process(file);
28571                     }).createDelegate(this)
28572                 );
28573         
28574                 return;
28575             }
28576             
28577             docs.push(
28578                 (function(){
28579                     _this.process(file);
28580                 }).createDelegate(this)
28581             );
28582             
28583         }, this);
28584         
28585         this.files = files;
28586         
28587         this.delegates = this.delegates.concat(docs);
28588         
28589         if(!this.delegates.length){
28590             this.refresh();
28591             return;
28592         }
28593         
28594         this.progressBar.aria_valuemax = this.delegates.length;
28595         
28596         this.arrange();
28597         
28598         return;
28599     },
28600     
28601     arrange : function()
28602     {
28603         if(!this.delegates.length){
28604             this.progressDialog.hide();
28605             this.refresh();
28606             return;
28607         }
28608         
28609         var delegate = this.delegates.shift();
28610         
28611         this.progressDialog.show();
28612         
28613         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28614         
28615         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28616         
28617         delegate();
28618     },
28619     
28620     refresh : function()
28621     {
28622         this.uploader.show();
28623         
28624         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28625             this.uploader.hide();
28626         }
28627         
28628         Roo.isTouch ? this.closable(false) : this.closable(true);
28629         
28630         this.fireEvent('refresh', this);
28631     },
28632     
28633     onRemove : function(e, el, o)
28634     {
28635         e.preventDefault();
28636         
28637         this.fireEvent('remove', this, o);
28638         
28639     },
28640     
28641     remove : function(o)
28642     {
28643         var files = [];
28644         
28645         Roo.each(this.files, function(file){
28646             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28647                 files.push(file);
28648                 return;
28649             }
28650
28651             o.target.remove();
28652
28653         }, this);
28654         
28655         this.files = files;
28656         
28657         this.refresh();
28658     },
28659     
28660     clear : function()
28661     {
28662         Roo.each(this.files, function(file){
28663             if(!file.target){
28664                 return;
28665             }
28666             
28667             file.target.remove();
28668
28669         }, this);
28670         
28671         this.files = [];
28672         
28673         this.refresh();
28674     },
28675     
28676     onClick : function(e, el, o)
28677     {
28678         e.preventDefault();
28679         
28680         this.fireEvent('click', this, o);
28681         
28682     },
28683     
28684     closable : function(closable)
28685     {
28686         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28687             
28688             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28689             
28690             if(closable){
28691                 el.show();
28692                 return;
28693             }
28694             
28695             el.hide();
28696             
28697         }, this);
28698     },
28699     
28700     xhrOnLoad : function(xhr)
28701     {
28702         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28703             el.remove();
28704         }, this);
28705         
28706         if (xhr.readyState !== 4) {
28707             this.arrange();
28708             this.fireEvent('exception', this, xhr);
28709             return;
28710         }
28711
28712         var response = Roo.decode(xhr.responseText);
28713         
28714         if(!response.success){
28715             this.arrange();
28716             this.fireEvent('exception', this, xhr);
28717             return;
28718         }
28719         
28720         var file = this.renderPreview(response.data);
28721         
28722         this.files.push(file);
28723         
28724         this.arrange();
28725         
28726         this.fireEvent('afterupload', this, xhr);
28727         
28728     },
28729     
28730     xhrOnError : function(xhr)
28731     {
28732         Roo.log('xhr on error');
28733         
28734         var response = Roo.decode(xhr.responseText);
28735           
28736         Roo.log(response);
28737         
28738         this.arrange();
28739     },
28740     
28741     process : function(file)
28742     {
28743         if(this.fireEvent('process', this, file) !== false){
28744             if(this.editable && file.type.indexOf('image') != -1){
28745                 this.fireEvent('edit', this, file);
28746                 return;
28747             }
28748
28749             this.uploadStart(file, false);
28750
28751             return;
28752         }
28753         
28754     },
28755     
28756     uploadStart : function(file, crop)
28757     {
28758         this.xhr = new XMLHttpRequest();
28759         
28760         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28761             this.arrange();
28762             return;
28763         }
28764         
28765         file.xhr = this.xhr;
28766             
28767         this.managerEl.createChild({
28768             tag : 'div',
28769             cls : 'roo-document-manager-loading',
28770             cn : [
28771                 {
28772                     tag : 'div',
28773                     tooltip : file.name,
28774                     cls : 'roo-document-manager-thumb',
28775                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28776                 }
28777             ]
28778
28779         });
28780
28781         this.xhr.open(this.method, this.url, true);
28782         
28783         var headers = {
28784             "Accept": "application/json",
28785             "Cache-Control": "no-cache",
28786             "X-Requested-With": "XMLHttpRequest"
28787         };
28788         
28789         for (var headerName in headers) {
28790             var headerValue = headers[headerName];
28791             if (headerValue) {
28792                 this.xhr.setRequestHeader(headerName, headerValue);
28793             }
28794         }
28795         
28796         var _this = this;
28797         
28798         this.xhr.onload = function()
28799         {
28800             _this.xhrOnLoad(_this.xhr);
28801         }
28802         
28803         this.xhr.onerror = function()
28804         {
28805             _this.xhrOnError(_this.xhr);
28806         }
28807         
28808         var formData = new FormData();
28809
28810         formData.append('returnHTML', 'NO');
28811         
28812         if(crop){
28813             formData.append('crop', crop);
28814         }
28815         
28816         formData.append(this.paramName, file, file.name);
28817         
28818         var options = {
28819             file : file, 
28820             manually : false
28821         };
28822         
28823         if(this.fireEvent('prepare', this, formData, options) != false){
28824             
28825             if(options.manually){
28826                 return;
28827             }
28828             
28829             this.xhr.send(formData);
28830             return;
28831         };
28832         
28833         this.uploadCancel();
28834     },
28835     
28836     uploadCancel : function()
28837     {
28838         if (this.xhr) {
28839             this.xhr.abort();
28840         }
28841         
28842         this.delegates = [];
28843         
28844         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28845             el.remove();
28846         }, this);
28847         
28848         this.arrange();
28849     },
28850     
28851     renderPreview : function(file)
28852     {
28853         if(typeof(file.target) != 'undefined' && file.target){
28854             return file;
28855         }
28856         
28857         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28858         
28859         var previewEl = this.managerEl.createChild({
28860             tag : 'div',
28861             cls : 'roo-document-manager-preview',
28862             cn : [
28863                 {
28864                     tag : 'div',
28865                     tooltip : file[this.toolTipName],
28866                     cls : 'roo-document-manager-thumb',
28867                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28868                 },
28869                 {
28870                     tag : 'button',
28871                     cls : 'close',
28872                     html : '<i class="fa fa-times-circle"></i>'
28873                 }
28874             ]
28875         });
28876
28877         var close = previewEl.select('button.close', true).first();
28878
28879         close.on('click', this.onRemove, this, file);
28880
28881         file.target = previewEl;
28882
28883         var image = previewEl.select('img', true).first();
28884         
28885         var _this = this;
28886         
28887         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28888         
28889         image.on('click', this.onClick, this, file);
28890         
28891         return file;
28892         
28893     },
28894     
28895     onPreviewLoad : function(file, image)
28896     {
28897         if(typeof(file.target) == 'undefined' || !file.target){
28898             return;
28899         }
28900         
28901         var width = image.dom.naturalWidth || image.dom.width;
28902         var height = image.dom.naturalHeight || image.dom.height;
28903         
28904         if(width > height){
28905             file.target.addClass('wide');
28906             return;
28907         }
28908         
28909         file.target.addClass('tall');
28910         return;
28911         
28912     },
28913     
28914     uploadFromSource : function(file, crop)
28915     {
28916         this.xhr = new XMLHttpRequest();
28917         
28918         this.managerEl.createChild({
28919             tag : 'div',
28920             cls : 'roo-document-manager-loading',
28921             cn : [
28922                 {
28923                     tag : 'div',
28924                     tooltip : file.name,
28925                     cls : 'roo-document-manager-thumb',
28926                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28927                 }
28928             ]
28929
28930         });
28931
28932         this.xhr.open(this.method, this.url, true);
28933         
28934         var headers = {
28935             "Accept": "application/json",
28936             "Cache-Control": "no-cache",
28937             "X-Requested-With": "XMLHttpRequest"
28938         };
28939         
28940         for (var headerName in headers) {
28941             var headerValue = headers[headerName];
28942             if (headerValue) {
28943                 this.xhr.setRequestHeader(headerName, headerValue);
28944             }
28945         }
28946         
28947         var _this = this;
28948         
28949         this.xhr.onload = function()
28950         {
28951             _this.xhrOnLoad(_this.xhr);
28952         }
28953         
28954         this.xhr.onerror = function()
28955         {
28956             _this.xhrOnError(_this.xhr);
28957         }
28958         
28959         var formData = new FormData();
28960
28961         formData.append('returnHTML', 'NO');
28962         
28963         formData.append('crop', crop);
28964         
28965         if(typeof(file.filename) != 'undefined'){
28966             formData.append('filename', file.filename);
28967         }
28968         
28969         if(typeof(file.mimetype) != 'undefined'){
28970             formData.append('mimetype', file.mimetype);
28971         }
28972         
28973         Roo.log(formData);
28974         
28975         if(this.fireEvent('prepare', this, formData) != false){
28976             this.xhr.send(formData);
28977         };
28978     }
28979 });
28980
28981 /*
28982 * Licence: LGPL
28983 */
28984
28985 /**
28986  * @class Roo.bootstrap.DocumentViewer
28987  * @extends Roo.bootstrap.Component
28988  * Bootstrap DocumentViewer class
28989  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28990  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28991  * 
28992  * @constructor
28993  * Create a new DocumentViewer
28994  * @param {Object} config The config object
28995  */
28996
28997 Roo.bootstrap.DocumentViewer = function(config){
28998     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28999     
29000     this.addEvents({
29001         /**
29002          * @event initial
29003          * Fire after initEvent
29004          * @param {Roo.bootstrap.DocumentViewer} this
29005          */
29006         "initial" : true,
29007         /**
29008          * @event click
29009          * Fire after click
29010          * @param {Roo.bootstrap.DocumentViewer} this
29011          */
29012         "click" : true,
29013         /**
29014          * @event download
29015          * Fire after download button
29016          * @param {Roo.bootstrap.DocumentViewer} this
29017          */
29018         "download" : true,
29019         /**
29020          * @event trash
29021          * Fire after trash button
29022          * @param {Roo.bootstrap.DocumentViewer} this
29023          */
29024         "trash" : true
29025         
29026     });
29027 };
29028
29029 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29030     
29031     showDownload : true,
29032     
29033     showTrash : true,
29034     
29035     getAutoCreate : function()
29036     {
29037         var cfg = {
29038             tag : 'div',
29039             cls : 'roo-document-viewer',
29040             cn : [
29041                 {
29042                     tag : 'div',
29043                     cls : 'roo-document-viewer-body',
29044                     cn : [
29045                         {
29046                             tag : 'div',
29047                             cls : 'roo-document-viewer-thumb',
29048                             cn : [
29049                                 {
29050                                     tag : 'img',
29051                                     cls : 'roo-document-viewer-image'
29052                                 }
29053                             ]
29054                         }
29055                     ]
29056                 },
29057                 {
29058                     tag : 'div',
29059                     cls : 'roo-document-viewer-footer',
29060                     cn : {
29061                         tag : 'div',
29062                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29063                         cn : [
29064                             {
29065                                 tag : 'div',
29066                                 cls : 'btn-group roo-document-viewer-download',
29067                                 cn : [
29068                                     {
29069                                         tag : 'button',
29070                                         cls : 'btn btn-default',
29071                                         html : '<i class="fa fa-download"></i>'
29072                                     }
29073                                 ]
29074                             },
29075                             {
29076                                 tag : 'div',
29077                                 cls : 'btn-group roo-document-viewer-trash',
29078                                 cn : [
29079                                     {
29080                                         tag : 'button',
29081                                         cls : 'btn btn-default',
29082                                         html : '<i class="fa fa-trash"></i>'
29083                                     }
29084                                 ]
29085                             }
29086                         ]
29087                     }
29088                 }
29089             ]
29090         };
29091         
29092         return cfg;
29093     },
29094     
29095     initEvents : function()
29096     {
29097         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29098         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29099         
29100         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29101         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29102         
29103         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29104         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29105         
29106         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29107         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29108         
29109         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29110         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29111         
29112         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29113         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29114         
29115         this.bodyEl.on('click', this.onClick, this);
29116         this.downloadBtn.on('click', this.onDownload, this);
29117         this.trashBtn.on('click', this.onTrash, this);
29118         
29119         this.downloadBtn.hide();
29120         this.trashBtn.hide();
29121         
29122         if(this.showDownload){
29123             this.downloadBtn.show();
29124         }
29125         
29126         if(this.showTrash){
29127             this.trashBtn.show();
29128         }
29129         
29130         if(!this.showDownload && !this.showTrash) {
29131             this.footerEl.hide();
29132         }
29133         
29134     },
29135     
29136     initial : function()
29137     {
29138         this.fireEvent('initial', this);
29139         
29140     },
29141     
29142     onClick : function(e)
29143     {
29144         e.preventDefault();
29145         
29146         this.fireEvent('click', this);
29147     },
29148     
29149     onDownload : function(e)
29150     {
29151         e.preventDefault();
29152         
29153         this.fireEvent('download', this);
29154     },
29155     
29156     onTrash : function(e)
29157     {
29158         e.preventDefault();
29159         
29160         this.fireEvent('trash', this);
29161     }
29162     
29163 });
29164 /*
29165  * - LGPL
29166  *
29167  * nav progress bar
29168  * 
29169  */
29170
29171 /**
29172  * @class Roo.bootstrap.NavProgressBar
29173  * @extends Roo.bootstrap.Component
29174  * Bootstrap NavProgressBar class
29175  * 
29176  * @constructor
29177  * Create a new nav progress bar
29178  * @param {Object} config The config object
29179  */
29180
29181 Roo.bootstrap.NavProgressBar = function(config){
29182     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29183
29184     this.bullets = this.bullets || [];
29185    
29186 //    Roo.bootstrap.NavProgressBar.register(this);
29187      this.addEvents({
29188         /**
29189              * @event changed
29190              * Fires when the active item changes
29191              * @param {Roo.bootstrap.NavProgressBar} this
29192              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29193              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29194          */
29195         'changed': true
29196      });
29197     
29198 };
29199
29200 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29201     
29202     bullets : [],
29203     barItems : [],
29204     
29205     getAutoCreate : function()
29206     {
29207         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29208         
29209         cfg = {
29210             tag : 'div',
29211             cls : 'roo-navigation-bar-group',
29212             cn : [
29213                 {
29214                     tag : 'div',
29215                     cls : 'roo-navigation-top-bar'
29216                 },
29217                 {
29218                     tag : 'div',
29219                     cls : 'roo-navigation-bullets-bar',
29220                     cn : [
29221                         {
29222                             tag : 'ul',
29223                             cls : 'roo-navigation-bar'
29224                         }
29225                     ]
29226                 },
29227                 
29228                 {
29229                     tag : 'div',
29230                     cls : 'roo-navigation-bottom-bar'
29231                 }
29232             ]
29233             
29234         };
29235         
29236         return cfg;
29237         
29238     },
29239     
29240     initEvents: function() 
29241     {
29242         
29243     },
29244     
29245     onRender : function(ct, position) 
29246     {
29247         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29248         
29249         if(this.bullets.length){
29250             Roo.each(this.bullets, function(b){
29251                this.addItem(b);
29252             }, this);
29253         }
29254         
29255         this.format();
29256         
29257     },
29258     
29259     addItem : function(cfg)
29260     {
29261         var item = new Roo.bootstrap.NavProgressItem(cfg);
29262         
29263         item.parentId = this.id;
29264         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29265         
29266         if(cfg.html){
29267             var top = new Roo.bootstrap.Element({
29268                 tag : 'div',
29269                 cls : 'roo-navigation-bar-text'
29270             });
29271             
29272             var bottom = new Roo.bootstrap.Element({
29273                 tag : 'div',
29274                 cls : 'roo-navigation-bar-text'
29275             });
29276             
29277             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29278             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29279             
29280             var topText = new Roo.bootstrap.Element({
29281                 tag : 'span',
29282                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29283             });
29284             
29285             var bottomText = new Roo.bootstrap.Element({
29286                 tag : 'span',
29287                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29288             });
29289             
29290             topText.onRender(top.el, null);
29291             bottomText.onRender(bottom.el, null);
29292             
29293             item.topEl = top;
29294             item.bottomEl = bottom;
29295         }
29296         
29297         this.barItems.push(item);
29298         
29299         return item;
29300     },
29301     
29302     getActive : function()
29303     {
29304         var active = false;
29305         
29306         Roo.each(this.barItems, function(v){
29307             
29308             if (!v.isActive()) {
29309                 return;
29310             }
29311             
29312             active = v;
29313             return false;
29314             
29315         });
29316         
29317         return active;
29318     },
29319     
29320     setActiveItem : function(item)
29321     {
29322         var prev = false;
29323         
29324         Roo.each(this.barItems, function(v){
29325             if (v.rid == item.rid) {
29326                 return ;
29327             }
29328             
29329             if (v.isActive()) {
29330                 v.setActive(false);
29331                 prev = v;
29332             }
29333         });
29334
29335         item.setActive(true);
29336         
29337         this.fireEvent('changed', this, item, prev);
29338     },
29339     
29340     getBarItem: function(rid)
29341     {
29342         var ret = false;
29343         
29344         Roo.each(this.barItems, function(e) {
29345             if (e.rid != rid) {
29346                 return;
29347             }
29348             
29349             ret =  e;
29350             return false;
29351         });
29352         
29353         return ret;
29354     },
29355     
29356     indexOfItem : function(item)
29357     {
29358         var index = false;
29359         
29360         Roo.each(this.barItems, function(v, i){
29361             
29362             if (v.rid != item.rid) {
29363                 return;
29364             }
29365             
29366             index = i;
29367             return false
29368         });
29369         
29370         return index;
29371     },
29372     
29373     setActiveNext : function()
29374     {
29375         var i = this.indexOfItem(this.getActive());
29376         
29377         if (i > this.barItems.length) {
29378             return;
29379         }
29380         
29381         this.setActiveItem(this.barItems[i+1]);
29382     },
29383     
29384     setActivePrev : function()
29385     {
29386         var i = this.indexOfItem(this.getActive());
29387         
29388         if (i  < 1) {
29389             return;
29390         }
29391         
29392         this.setActiveItem(this.barItems[i-1]);
29393     },
29394     
29395     format : function()
29396     {
29397         if(!this.barItems.length){
29398             return;
29399         }
29400      
29401         var width = 100 / this.barItems.length;
29402         
29403         Roo.each(this.barItems, function(i){
29404             i.el.setStyle('width', width + '%');
29405             i.topEl.el.setStyle('width', width + '%');
29406             i.bottomEl.el.setStyle('width', width + '%');
29407         }, this);
29408         
29409     }
29410     
29411 });
29412 /*
29413  * - LGPL
29414  *
29415  * Nav Progress Item
29416  * 
29417  */
29418
29419 /**
29420  * @class Roo.bootstrap.NavProgressItem
29421  * @extends Roo.bootstrap.Component
29422  * Bootstrap NavProgressItem class
29423  * @cfg {String} rid the reference id
29424  * @cfg {Boolean} active (true|false) Is item active default false
29425  * @cfg {Boolean} disabled (true|false) Is item active default false
29426  * @cfg {String} html
29427  * @cfg {String} position (top|bottom) text position default bottom
29428  * @cfg {String} icon show icon instead of number
29429  * 
29430  * @constructor
29431  * Create a new NavProgressItem
29432  * @param {Object} config The config object
29433  */
29434 Roo.bootstrap.NavProgressItem = function(config){
29435     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29436     this.addEvents({
29437         // raw events
29438         /**
29439          * @event click
29440          * The raw click event for the entire grid.
29441          * @param {Roo.bootstrap.NavProgressItem} this
29442          * @param {Roo.EventObject} e
29443          */
29444         "click" : true
29445     });
29446    
29447 };
29448
29449 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29450     
29451     rid : '',
29452     active : false,
29453     disabled : false,
29454     html : '',
29455     position : 'bottom',
29456     icon : false,
29457     
29458     getAutoCreate : function()
29459     {
29460         var iconCls = 'roo-navigation-bar-item-icon';
29461         
29462         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29463         
29464         var cfg = {
29465             tag: 'li',
29466             cls: 'roo-navigation-bar-item',
29467             cn : [
29468                 {
29469                     tag : 'i',
29470                     cls : iconCls
29471                 }
29472             ]
29473         };
29474         
29475         if(this.active){
29476             cfg.cls += ' active';
29477         }
29478         if(this.disabled){
29479             cfg.cls += ' disabled';
29480         }
29481         
29482         return cfg;
29483     },
29484     
29485     disable : function()
29486     {
29487         this.setDisabled(true);
29488     },
29489     
29490     enable : function()
29491     {
29492         this.setDisabled(false);
29493     },
29494     
29495     initEvents: function() 
29496     {
29497         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29498         
29499         this.iconEl.on('click', this.onClick, this);
29500     },
29501     
29502     onClick : function(e)
29503     {
29504         e.preventDefault();
29505         
29506         if(this.disabled){
29507             return;
29508         }
29509         
29510         if(this.fireEvent('click', this, e) === false){
29511             return;
29512         };
29513         
29514         this.parent().setActiveItem(this);
29515     },
29516     
29517     isActive: function () 
29518     {
29519         return this.active;
29520     },
29521     
29522     setActive : function(state)
29523     {
29524         if(this.active == state){
29525             return;
29526         }
29527         
29528         this.active = state;
29529         
29530         if (state) {
29531             this.el.addClass('active');
29532             return;
29533         }
29534         
29535         this.el.removeClass('active');
29536         
29537         return;
29538     },
29539     
29540     setDisabled : function(state)
29541     {
29542         if(this.disabled == state){
29543             return;
29544         }
29545         
29546         this.disabled = state;
29547         
29548         if (state) {
29549             this.el.addClass('disabled');
29550             return;
29551         }
29552         
29553         this.el.removeClass('disabled');
29554     },
29555     
29556     tooltipEl : function()
29557     {
29558         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29559     }
29560 });
29561  
29562
29563  /*
29564  * - LGPL
29565  *
29566  * FieldLabel
29567  * 
29568  */
29569
29570 /**
29571  * @class Roo.bootstrap.FieldLabel
29572  * @extends Roo.bootstrap.Component
29573  * Bootstrap FieldLabel class
29574  * @cfg {String} html contents of the element
29575  * @cfg {String} tag tag of the element default label
29576  * @cfg {String} cls class of the element
29577  * @cfg {String} target label target 
29578  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29579  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29580  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29581  * @cfg {String} iconTooltip default "This field is required"
29582  * 
29583  * @constructor
29584  * Create a new FieldLabel
29585  * @param {Object} config The config object
29586  */
29587
29588 Roo.bootstrap.FieldLabel = function(config){
29589     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29590     
29591     this.addEvents({
29592             /**
29593              * @event invalid
29594              * Fires after the field has been marked as invalid.
29595              * @param {Roo.form.FieldLabel} this
29596              * @param {String} msg The validation message
29597              */
29598             invalid : true,
29599             /**
29600              * @event valid
29601              * Fires after the field has been validated with no errors.
29602              * @param {Roo.form.FieldLabel} this
29603              */
29604             valid : true
29605         });
29606 };
29607
29608 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29609     
29610     tag: 'label',
29611     cls: '',
29612     html: '',
29613     target: '',
29614     allowBlank : true,
29615     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29616     validClass : 'text-success fa fa-lg fa-check',
29617     iconTooltip : 'This field is required',
29618     
29619     getAutoCreate : function(){
29620         
29621         var cfg = {
29622             tag : this.tag,
29623             cls : 'roo-bootstrap-field-label ' + this.cls,
29624             for : this.target,
29625             cn : [
29626                 {
29627                     tag : 'i',
29628                     cls : '',
29629                     tooltip : this.iconTooltip
29630                 },
29631                 {
29632                     tag : 'span',
29633                     html : this.html
29634                 }
29635             ] 
29636         };
29637         
29638         return cfg;
29639     },
29640     
29641     initEvents: function() 
29642     {
29643         Roo.bootstrap.Element.superclass.initEvents.call(this);
29644         
29645         this.iconEl = this.el.select('i', true).first();
29646         
29647         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29648         
29649         Roo.bootstrap.FieldLabel.register(this);
29650     },
29651     
29652     /**
29653      * Mark this field as valid
29654      */
29655     markValid : function()
29656     {
29657         this.iconEl.show();
29658         
29659         this.iconEl.removeClass(this.invalidClass);
29660         
29661         this.iconEl.addClass(this.validClass);
29662         
29663         this.fireEvent('valid', this);
29664     },
29665     
29666     /**
29667      * Mark this field as invalid
29668      * @param {String} msg The validation message
29669      */
29670     markInvalid : function(msg)
29671     {
29672         this.iconEl.show();
29673         
29674         this.iconEl.removeClass(this.validClass);
29675         
29676         this.iconEl.addClass(this.invalidClass);
29677         
29678         this.fireEvent('invalid', this, msg);
29679     }
29680     
29681    
29682 });
29683
29684 Roo.apply(Roo.bootstrap.FieldLabel, {
29685     
29686     groups: {},
29687     
29688      /**
29689     * register a FieldLabel Group
29690     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29691     */
29692     register : function(label)
29693     {
29694         if(this.groups.hasOwnProperty(label.target)){
29695             return;
29696         }
29697      
29698         this.groups[label.target] = label;
29699         
29700     },
29701     /**
29702     * fetch a FieldLabel Group based on the target
29703     * @param {string} target
29704     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29705     */
29706     get: function(target) {
29707         if (typeof(this.groups[target]) == 'undefined') {
29708             return false;
29709         }
29710         
29711         return this.groups[target] ;
29712     }
29713 });
29714
29715  
29716
29717  /*
29718  * - LGPL
29719  *
29720  * page DateSplitField.
29721  * 
29722  */
29723
29724
29725 /**
29726  * @class Roo.bootstrap.DateSplitField
29727  * @extends Roo.bootstrap.Component
29728  * Bootstrap DateSplitField class
29729  * @cfg {string} fieldLabel - the label associated
29730  * @cfg {Number} labelWidth set the width of label (0-12)
29731  * @cfg {String} labelAlign (top|left)
29732  * @cfg {Boolean} dayAllowBlank (true|false) default false
29733  * @cfg {Boolean} monthAllowBlank (true|false) default false
29734  * @cfg {Boolean} yearAllowBlank (true|false) default false
29735  * @cfg {string} dayPlaceholder 
29736  * @cfg {string} monthPlaceholder
29737  * @cfg {string} yearPlaceholder
29738  * @cfg {string} dayFormat default 'd'
29739  * @cfg {string} monthFormat default 'm'
29740  * @cfg {string} yearFormat default 'Y'
29741  * @cfg {Number} labellg set the width of label (1-12)
29742  * @cfg {Number} labelmd set the width of label (1-12)
29743  * @cfg {Number} labelsm set the width of label (1-12)
29744  * @cfg {Number} labelxs set the width of label (1-12)
29745
29746  *     
29747  * @constructor
29748  * Create a new DateSplitField
29749  * @param {Object} config The config object
29750  */
29751
29752 Roo.bootstrap.DateSplitField = function(config){
29753     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29754     
29755     this.addEvents({
29756         // raw events
29757          /**
29758          * @event years
29759          * getting the data of years
29760          * @param {Roo.bootstrap.DateSplitField} this
29761          * @param {Object} years
29762          */
29763         "years" : true,
29764         /**
29765          * @event days
29766          * getting the data of days
29767          * @param {Roo.bootstrap.DateSplitField} this
29768          * @param {Object} days
29769          */
29770         "days" : true,
29771         /**
29772          * @event invalid
29773          * Fires after the field has been marked as invalid.
29774          * @param {Roo.form.Field} this
29775          * @param {String} msg The validation message
29776          */
29777         invalid : true,
29778        /**
29779          * @event valid
29780          * Fires after the field has been validated with no errors.
29781          * @param {Roo.form.Field} this
29782          */
29783         valid : true
29784     });
29785 };
29786
29787 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29788     
29789     fieldLabel : '',
29790     labelAlign : 'top',
29791     labelWidth : 3,
29792     dayAllowBlank : false,
29793     monthAllowBlank : false,
29794     yearAllowBlank : false,
29795     dayPlaceholder : '',
29796     monthPlaceholder : '',
29797     yearPlaceholder : '',
29798     dayFormat : 'd',
29799     monthFormat : 'm',
29800     yearFormat : 'Y',
29801     isFormField : true,
29802     labellg : 0,
29803     labelmd : 0,
29804     labelsm : 0,
29805     labelxs : 0,
29806     
29807     getAutoCreate : function()
29808     {
29809         var cfg = {
29810             tag : 'div',
29811             cls : 'row roo-date-split-field-group',
29812             cn : [
29813                 {
29814                     tag : 'input',
29815                     type : 'hidden',
29816                     cls : 'form-hidden-field roo-date-split-field-group-value',
29817                     name : this.name
29818                 }
29819             ]
29820         };
29821         
29822         var labelCls = 'col-md-12';
29823         var contentCls = 'col-md-4';
29824         
29825         if(this.fieldLabel){
29826             
29827             var label = {
29828                 tag : 'div',
29829                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29830                 cn : [
29831                     {
29832                         tag : 'label',
29833                         html : this.fieldLabel
29834                     }
29835                 ]
29836             };
29837             
29838             if(this.labelAlign == 'left'){
29839             
29840                 if(this.labelWidth > 12){
29841                     label.style = "width: " + this.labelWidth + 'px';
29842                 }
29843
29844                 if(this.labelWidth < 13 && this.labelmd == 0){
29845                     this.labelmd = this.labelWidth;
29846                 }
29847
29848                 if(this.labellg > 0){
29849                     labelCls = ' col-lg-' + this.labellg;
29850                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29851                 }
29852
29853                 if(this.labelmd > 0){
29854                     labelCls = ' col-md-' + this.labelmd;
29855                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29856                 }
29857
29858                 if(this.labelsm > 0){
29859                     labelCls = ' col-sm-' + this.labelsm;
29860                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29861                 }
29862
29863                 if(this.labelxs > 0){
29864                     labelCls = ' col-xs-' + this.labelxs;
29865                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29866                 }
29867             }
29868             
29869             label.cls += ' ' + labelCls;
29870             
29871             cfg.cn.push(label);
29872         }
29873         
29874         Roo.each(['day', 'month', 'year'], function(t){
29875             cfg.cn.push({
29876                 tag : 'div',
29877                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29878             });
29879         }, this);
29880         
29881         return cfg;
29882     },
29883     
29884     inputEl: function ()
29885     {
29886         return this.el.select('.roo-date-split-field-group-value', true).first();
29887     },
29888     
29889     onRender : function(ct, position) 
29890     {
29891         var _this = this;
29892         
29893         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29894         
29895         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29896         
29897         this.dayField = new Roo.bootstrap.ComboBox({
29898             allowBlank : this.dayAllowBlank,
29899             alwaysQuery : true,
29900             displayField : 'value',
29901             editable : false,
29902             fieldLabel : '',
29903             forceSelection : true,
29904             mode : 'local',
29905             placeholder : this.dayPlaceholder,
29906             selectOnFocus : true,
29907             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29908             triggerAction : 'all',
29909             typeAhead : true,
29910             valueField : 'value',
29911             store : new Roo.data.SimpleStore({
29912                 data : (function() {    
29913                     var days = [];
29914                     _this.fireEvent('days', _this, days);
29915                     return days;
29916                 })(),
29917                 fields : [ 'value' ]
29918             }),
29919             listeners : {
29920                 select : function (_self, record, index)
29921                 {
29922                     _this.setValue(_this.getValue());
29923                 }
29924             }
29925         });
29926
29927         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29928         
29929         this.monthField = new Roo.bootstrap.MonthField({
29930             after : '<i class=\"fa fa-calendar\"></i>',
29931             allowBlank : this.monthAllowBlank,
29932             placeholder : this.monthPlaceholder,
29933             readOnly : true,
29934             listeners : {
29935                 render : function (_self)
29936                 {
29937                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29938                         e.preventDefault();
29939                         _self.focus();
29940                     });
29941                 },
29942                 select : function (_self, oldvalue, newvalue)
29943                 {
29944                     _this.setValue(_this.getValue());
29945                 }
29946             }
29947         });
29948         
29949         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29950         
29951         this.yearField = new Roo.bootstrap.ComboBox({
29952             allowBlank : this.yearAllowBlank,
29953             alwaysQuery : true,
29954             displayField : 'value',
29955             editable : false,
29956             fieldLabel : '',
29957             forceSelection : true,
29958             mode : 'local',
29959             placeholder : this.yearPlaceholder,
29960             selectOnFocus : true,
29961             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29962             triggerAction : 'all',
29963             typeAhead : true,
29964             valueField : 'value',
29965             store : new Roo.data.SimpleStore({
29966                 data : (function() {
29967                     var years = [];
29968                     _this.fireEvent('years', _this, years);
29969                     return years;
29970                 })(),
29971                 fields : [ 'value' ]
29972             }),
29973             listeners : {
29974                 select : function (_self, record, index)
29975                 {
29976                     _this.setValue(_this.getValue());
29977                 }
29978             }
29979         });
29980
29981         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29982     },
29983     
29984     setValue : function(v, format)
29985     {
29986         this.inputEl.dom.value = v;
29987         
29988         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29989         
29990         var d = Date.parseDate(v, f);
29991         
29992         if(!d){
29993             this.validate();
29994             return;
29995         }
29996         
29997         this.setDay(d.format(this.dayFormat));
29998         this.setMonth(d.format(this.monthFormat));
29999         this.setYear(d.format(this.yearFormat));
30000         
30001         this.validate();
30002         
30003         return;
30004     },
30005     
30006     setDay : function(v)
30007     {
30008         this.dayField.setValue(v);
30009         this.inputEl.dom.value = this.getValue();
30010         this.validate();
30011         return;
30012     },
30013     
30014     setMonth : function(v)
30015     {
30016         this.monthField.setValue(v, true);
30017         this.inputEl.dom.value = this.getValue();
30018         this.validate();
30019         return;
30020     },
30021     
30022     setYear : function(v)
30023     {
30024         this.yearField.setValue(v);
30025         this.inputEl.dom.value = this.getValue();
30026         this.validate();
30027         return;
30028     },
30029     
30030     getDay : function()
30031     {
30032         return this.dayField.getValue();
30033     },
30034     
30035     getMonth : function()
30036     {
30037         return this.monthField.getValue();
30038     },
30039     
30040     getYear : function()
30041     {
30042         return this.yearField.getValue();
30043     },
30044     
30045     getValue : function()
30046     {
30047         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30048         
30049         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30050         
30051         return date;
30052     },
30053     
30054     reset : function()
30055     {
30056         this.setDay('');
30057         this.setMonth('');
30058         this.setYear('');
30059         this.inputEl.dom.value = '';
30060         this.validate();
30061         return;
30062     },
30063     
30064     validate : function()
30065     {
30066         var d = this.dayField.validate();
30067         var m = this.monthField.validate();
30068         var y = this.yearField.validate();
30069         
30070         var valid = true;
30071         
30072         if(
30073                 (!this.dayAllowBlank && !d) ||
30074                 (!this.monthAllowBlank && !m) ||
30075                 (!this.yearAllowBlank && !y)
30076         ){
30077             valid = false;
30078         }
30079         
30080         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30081             return valid;
30082         }
30083         
30084         if(valid){
30085             this.markValid();
30086             return valid;
30087         }
30088         
30089         this.markInvalid();
30090         
30091         return valid;
30092     },
30093     
30094     markValid : function()
30095     {
30096         
30097         var label = this.el.select('label', true).first();
30098         var icon = this.el.select('i.fa-star', true).first();
30099
30100         if(label && icon){
30101             icon.remove();
30102         }
30103         
30104         this.fireEvent('valid', this);
30105     },
30106     
30107      /**
30108      * Mark this field as invalid
30109      * @param {String} msg The validation message
30110      */
30111     markInvalid : function(msg)
30112     {
30113         
30114         var label = this.el.select('label', true).first();
30115         var icon = this.el.select('i.fa-star', true).first();
30116
30117         if(label && !icon){
30118             this.el.select('.roo-date-split-field-label', true).createChild({
30119                 tag : 'i',
30120                 cls : 'text-danger fa fa-lg fa-star',
30121                 tooltip : 'This field is required',
30122                 style : 'margin-right:5px;'
30123             }, label, true);
30124         }
30125         
30126         this.fireEvent('invalid', this, msg);
30127     },
30128     
30129     clearInvalid : function()
30130     {
30131         var label = this.el.select('label', true).first();
30132         var icon = this.el.select('i.fa-star', true).first();
30133
30134         if(label && icon){
30135             icon.remove();
30136         }
30137         
30138         this.fireEvent('valid', this);
30139     },
30140     
30141     getName: function()
30142     {
30143         return this.name;
30144     }
30145     
30146 });
30147
30148  /**
30149  *
30150  * This is based on 
30151  * http://masonry.desandro.com
30152  *
30153  * The idea is to render all the bricks based on vertical width...
30154  *
30155  * The original code extends 'outlayer' - we might need to use that....
30156  * 
30157  */
30158
30159
30160 /**
30161  * @class Roo.bootstrap.LayoutMasonry
30162  * @extends Roo.bootstrap.Component
30163  * Bootstrap Layout Masonry class
30164  * 
30165  * @constructor
30166  * Create a new Element
30167  * @param {Object} config The config object
30168  */
30169
30170 Roo.bootstrap.LayoutMasonry = function(config){
30171     
30172     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30173     
30174     this.bricks = [];
30175     
30176     Roo.bootstrap.LayoutMasonry.register(this);
30177     
30178     this.addEvents({
30179         // raw events
30180         /**
30181          * @event layout
30182          * Fire after layout the items
30183          * @param {Roo.bootstrap.LayoutMasonry} this
30184          * @param {Roo.EventObject} e
30185          */
30186         "layout" : true
30187     });
30188     
30189 };
30190
30191 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30192     
30193     /**
30194      * @cfg {Boolean} isLayoutInstant = no animation?
30195      */   
30196     isLayoutInstant : false, // needed?
30197    
30198     /**
30199      * @cfg {Number} boxWidth  width of the columns
30200      */   
30201     boxWidth : 450,
30202     
30203       /**
30204      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30205      */   
30206     boxHeight : 0,
30207     
30208     /**
30209      * @cfg {Number} padWidth padding below box..
30210      */   
30211     padWidth : 10, 
30212     
30213     /**
30214      * @cfg {Number} gutter gutter width..
30215      */   
30216     gutter : 10,
30217     
30218      /**
30219      * @cfg {Number} maxCols maximum number of columns
30220      */   
30221     
30222     maxCols: 0,
30223     
30224     /**
30225      * @cfg {Boolean} isAutoInitial defalut true
30226      */   
30227     isAutoInitial : true, 
30228     
30229     containerWidth: 0,
30230     
30231     /**
30232      * @cfg {Boolean} isHorizontal defalut false
30233      */   
30234     isHorizontal : false, 
30235
30236     currentSize : null,
30237     
30238     tag: 'div',
30239     
30240     cls: '',
30241     
30242     bricks: null, //CompositeElement
30243     
30244     cols : 1,
30245     
30246     _isLayoutInited : false,
30247     
30248 //    isAlternative : false, // only use for vertical layout...
30249     
30250     /**
30251      * @cfg {Number} alternativePadWidth padding below box..
30252      */   
30253     alternativePadWidth : 50,
30254     
30255     selectedBrick : [],
30256     
30257     getAutoCreate : function(){
30258         
30259         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30260         
30261         var cfg = {
30262             tag: this.tag,
30263             cls: 'blog-masonary-wrapper ' + this.cls,
30264             cn : {
30265                 cls : 'mas-boxes masonary'
30266             }
30267         };
30268         
30269         return cfg;
30270     },
30271     
30272     getChildContainer: function( )
30273     {
30274         if (this.boxesEl) {
30275             return this.boxesEl;
30276         }
30277         
30278         this.boxesEl = this.el.select('.mas-boxes').first();
30279         
30280         return this.boxesEl;
30281     },
30282     
30283     
30284     initEvents : function()
30285     {
30286         var _this = this;
30287         
30288         if(this.isAutoInitial){
30289             Roo.log('hook children rendered');
30290             this.on('childrenrendered', function() {
30291                 Roo.log('children rendered');
30292                 _this.initial();
30293             } ,this);
30294         }
30295     },
30296     
30297     initial : function()
30298     {
30299         this.selectedBrick = [];
30300         
30301         this.currentSize = this.el.getBox(true);
30302         
30303         Roo.EventManager.onWindowResize(this.resize, this); 
30304
30305         if(!this.isAutoInitial){
30306             this.layout();
30307             return;
30308         }
30309         
30310         this.layout();
30311         
30312         return;
30313         //this.layout.defer(500,this);
30314         
30315     },
30316     
30317     resize : function()
30318     {
30319         var cs = this.el.getBox(true);
30320         
30321         if (
30322                 this.currentSize.width == cs.width && 
30323                 this.currentSize.x == cs.x && 
30324                 this.currentSize.height == cs.height && 
30325                 this.currentSize.y == cs.y 
30326         ) {
30327             Roo.log("no change in with or X or Y");
30328             return;
30329         }
30330         
30331         this.currentSize = cs;
30332         
30333         this.layout();
30334         
30335     },
30336     
30337     layout : function()
30338     {   
30339         this._resetLayout();
30340         
30341         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30342         
30343         this.layoutItems( isInstant );
30344       
30345         this._isLayoutInited = true;
30346         
30347         this.fireEvent('layout', this);
30348         
30349     },
30350     
30351     _resetLayout : function()
30352     {
30353         if(this.isHorizontal){
30354             this.horizontalMeasureColumns();
30355             return;
30356         }
30357         
30358         this.verticalMeasureColumns();
30359         
30360     },
30361     
30362     verticalMeasureColumns : function()
30363     {
30364         this.getContainerWidth();
30365         
30366 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30367 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30368 //            return;
30369 //        }
30370         
30371         var boxWidth = this.boxWidth + this.padWidth;
30372         
30373         if(this.containerWidth < this.boxWidth){
30374             boxWidth = this.containerWidth
30375         }
30376         
30377         var containerWidth = this.containerWidth;
30378         
30379         var cols = Math.floor(containerWidth / boxWidth);
30380         
30381         this.cols = Math.max( cols, 1 );
30382         
30383         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30384         
30385         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30386         
30387         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30388         
30389         this.colWidth = boxWidth + avail - this.padWidth;
30390         
30391         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30392         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30393     },
30394     
30395     horizontalMeasureColumns : function()
30396     {
30397         this.getContainerWidth();
30398         
30399         var boxWidth = this.boxWidth;
30400         
30401         if(this.containerWidth < boxWidth){
30402             boxWidth = this.containerWidth;
30403         }
30404         
30405         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30406         
30407         this.el.setHeight(boxWidth);
30408         
30409     },
30410     
30411     getContainerWidth : function()
30412     {
30413         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30414     },
30415     
30416     layoutItems : function( isInstant )
30417     {
30418         Roo.log(this.bricks);
30419         
30420         var items = Roo.apply([], this.bricks);
30421         
30422         if(this.isHorizontal){
30423             this._horizontalLayoutItems( items , isInstant );
30424             return;
30425         }
30426         
30427 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30428 //            this._verticalAlternativeLayoutItems( items , isInstant );
30429 //            return;
30430 //        }
30431         
30432         this._verticalLayoutItems( items , isInstant );
30433         
30434     },
30435     
30436     _verticalLayoutItems : function ( items , isInstant)
30437     {
30438         if ( !items || !items.length ) {
30439             return;
30440         }
30441         
30442         var standard = [
30443             ['xs', 'xs', 'xs', 'tall'],
30444             ['xs', 'xs', 'tall'],
30445             ['xs', 'xs', 'sm'],
30446             ['xs', 'xs', 'xs'],
30447             ['xs', 'tall'],
30448             ['xs', 'sm'],
30449             ['xs', 'xs'],
30450             ['xs'],
30451             
30452             ['sm', 'xs', 'xs'],
30453             ['sm', 'xs'],
30454             ['sm'],
30455             
30456             ['tall', 'xs', 'xs', 'xs'],
30457             ['tall', 'xs', 'xs'],
30458             ['tall', 'xs'],
30459             ['tall']
30460             
30461         ];
30462         
30463         var queue = [];
30464         
30465         var boxes = [];
30466         
30467         var box = [];
30468         
30469         Roo.each(items, function(item, k){
30470             
30471             switch (item.size) {
30472                 // these layouts take up a full box,
30473                 case 'md' :
30474                 case 'md-left' :
30475                 case 'md-right' :
30476                 case 'wide' :
30477                     
30478                     if(box.length){
30479                         boxes.push(box);
30480                         box = [];
30481                     }
30482                     
30483                     boxes.push([item]);
30484                     
30485                     break;
30486                     
30487                 case 'xs' :
30488                 case 'sm' :
30489                 case 'tall' :
30490                     
30491                     box.push(item);
30492                     
30493                     break;
30494                 default :
30495                     break;
30496                     
30497             }
30498             
30499         }, this);
30500         
30501         if(box.length){
30502             boxes.push(box);
30503             box = [];
30504         }
30505         
30506         var filterPattern = function(box, length)
30507         {
30508             if(!box.length){
30509                 return;
30510             }
30511             
30512             var match = false;
30513             
30514             var pattern = box.slice(0, length);
30515             
30516             var format = [];
30517             
30518             Roo.each(pattern, function(i){
30519                 format.push(i.size);
30520             }, this);
30521             
30522             Roo.each(standard, function(s){
30523                 
30524                 if(String(s) != String(format)){
30525                     return;
30526                 }
30527                 
30528                 match = true;
30529                 return false;
30530                 
30531             }, this);
30532             
30533             if(!match && length == 1){
30534                 return;
30535             }
30536             
30537             if(!match){
30538                 filterPattern(box, length - 1);
30539                 return;
30540             }
30541                 
30542             queue.push(pattern);
30543
30544             box = box.slice(length, box.length);
30545
30546             filterPattern(box, 4);
30547
30548             return;
30549             
30550         }
30551         
30552         Roo.each(boxes, function(box, k){
30553             
30554             if(!box.length){
30555                 return;
30556             }
30557             
30558             if(box.length == 1){
30559                 queue.push(box);
30560                 return;
30561             }
30562             
30563             filterPattern(box, 4);
30564             
30565         }, this);
30566         
30567         this._processVerticalLayoutQueue( queue, isInstant );
30568         
30569     },
30570     
30571 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30572 //    {
30573 //        if ( !items || !items.length ) {
30574 //            return;
30575 //        }
30576 //
30577 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30578 //        
30579 //    },
30580     
30581     _horizontalLayoutItems : function ( items , isInstant)
30582     {
30583         if ( !items || !items.length || items.length < 3) {
30584             return;
30585         }
30586         
30587         items.reverse();
30588         
30589         var eItems = items.slice(0, 3);
30590         
30591         items = items.slice(3, items.length);
30592         
30593         var standard = [
30594             ['xs', 'xs', 'xs', 'wide'],
30595             ['xs', 'xs', 'wide'],
30596             ['xs', 'xs', 'sm'],
30597             ['xs', 'xs', 'xs'],
30598             ['xs', 'wide'],
30599             ['xs', 'sm'],
30600             ['xs', 'xs'],
30601             ['xs'],
30602             
30603             ['sm', 'xs', 'xs'],
30604             ['sm', 'xs'],
30605             ['sm'],
30606             
30607             ['wide', 'xs', 'xs', 'xs'],
30608             ['wide', 'xs', 'xs'],
30609             ['wide', 'xs'],
30610             ['wide'],
30611             
30612             ['wide-thin']
30613         ];
30614         
30615         var queue = [];
30616         
30617         var boxes = [];
30618         
30619         var box = [];
30620         
30621         Roo.each(items, function(item, k){
30622             
30623             switch (item.size) {
30624                 case 'md' :
30625                 case 'md-left' :
30626                 case 'md-right' :
30627                 case 'tall' :
30628                     
30629                     if(box.length){
30630                         boxes.push(box);
30631                         box = [];
30632                     }
30633                     
30634                     boxes.push([item]);
30635                     
30636                     break;
30637                     
30638                 case 'xs' :
30639                 case 'sm' :
30640                 case 'wide' :
30641                 case 'wide-thin' :
30642                     
30643                     box.push(item);
30644                     
30645                     break;
30646                 default :
30647                     break;
30648                     
30649             }
30650             
30651         }, this);
30652         
30653         if(box.length){
30654             boxes.push(box);
30655             box = [];
30656         }
30657         
30658         var filterPattern = function(box, length)
30659         {
30660             if(!box.length){
30661                 return;
30662             }
30663             
30664             var match = false;
30665             
30666             var pattern = box.slice(0, length);
30667             
30668             var format = [];
30669             
30670             Roo.each(pattern, function(i){
30671                 format.push(i.size);
30672             }, this);
30673             
30674             Roo.each(standard, function(s){
30675                 
30676                 if(String(s) != String(format)){
30677                     return;
30678                 }
30679                 
30680                 match = true;
30681                 return false;
30682                 
30683             }, this);
30684             
30685             if(!match && length == 1){
30686                 return;
30687             }
30688             
30689             if(!match){
30690                 filterPattern(box, length - 1);
30691                 return;
30692             }
30693                 
30694             queue.push(pattern);
30695
30696             box = box.slice(length, box.length);
30697
30698             filterPattern(box, 4);
30699
30700             return;
30701             
30702         }
30703         
30704         Roo.each(boxes, function(box, k){
30705             
30706             if(!box.length){
30707                 return;
30708             }
30709             
30710             if(box.length == 1){
30711                 queue.push(box);
30712                 return;
30713             }
30714             
30715             filterPattern(box, 4);
30716             
30717         }, this);
30718         
30719         
30720         var prune = [];
30721         
30722         var pos = this.el.getBox(true);
30723         
30724         var minX = pos.x;
30725         
30726         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30727         
30728         var hit_end = false;
30729         
30730         Roo.each(queue, function(box){
30731             
30732             if(hit_end){
30733                 
30734                 Roo.each(box, function(b){
30735                 
30736                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30737                     b.el.hide();
30738
30739                 }, this);
30740
30741                 return;
30742             }
30743             
30744             var mx = 0;
30745             
30746             Roo.each(box, function(b){
30747                 
30748                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30749                 b.el.show();
30750
30751                 mx = Math.max(mx, b.x);
30752                 
30753             }, this);
30754             
30755             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30756             
30757             if(maxX < minX){
30758                 
30759                 Roo.each(box, function(b){
30760                 
30761                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30762                     b.el.hide();
30763                     
30764                 }, this);
30765                 
30766                 hit_end = true;
30767                 
30768                 return;
30769             }
30770             
30771             prune.push(box);
30772             
30773         }, this);
30774         
30775         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30776     },
30777     
30778     /** Sets position of item in DOM
30779     * @param {Element} item
30780     * @param {Number} x - horizontal position
30781     * @param {Number} y - vertical position
30782     * @param {Boolean} isInstant - disables transitions
30783     */
30784     _processVerticalLayoutQueue : function( queue, isInstant )
30785     {
30786         var pos = this.el.getBox(true);
30787         var x = pos.x;
30788         var y = pos.y;
30789         var maxY = [];
30790         
30791         for (var i = 0; i < this.cols; i++){
30792             maxY[i] = pos.y;
30793         }
30794         
30795         Roo.each(queue, function(box, k){
30796             
30797             var col = k % this.cols;
30798             
30799             Roo.each(box, function(b,kk){
30800                 
30801                 b.el.position('absolute');
30802                 
30803                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30804                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30805                 
30806                 if(b.size == 'md-left' || b.size == 'md-right'){
30807                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30808                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30809                 }
30810                 
30811                 b.el.setWidth(width);
30812                 b.el.setHeight(height);
30813                 // iframe?
30814                 b.el.select('iframe',true).setSize(width,height);
30815                 
30816             }, this);
30817             
30818             for (var i = 0; i < this.cols; i++){
30819                 
30820                 if(maxY[i] < maxY[col]){
30821                     col = i;
30822                     continue;
30823                 }
30824                 
30825                 col = Math.min(col, i);
30826                 
30827             }
30828             
30829             x = pos.x + col * (this.colWidth + this.padWidth);
30830             
30831             y = maxY[col];
30832             
30833             var positions = [];
30834             
30835             switch (box.length){
30836                 case 1 :
30837                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30838                     break;
30839                 case 2 :
30840                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30841                     break;
30842                 case 3 :
30843                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30844                     break;
30845                 case 4 :
30846                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30847                     break;
30848                 default :
30849                     break;
30850             }
30851             
30852             Roo.each(box, function(b,kk){
30853                 
30854                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30855                 
30856                 var sz = b.el.getSize();
30857                 
30858                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30859                 
30860             }, this);
30861             
30862         }, this);
30863         
30864         var mY = 0;
30865         
30866         for (var i = 0; i < this.cols; i++){
30867             mY = Math.max(mY, maxY[i]);
30868         }
30869         
30870         this.el.setHeight(mY - pos.y);
30871         
30872     },
30873     
30874 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30875 //    {
30876 //        var pos = this.el.getBox(true);
30877 //        var x = pos.x;
30878 //        var y = pos.y;
30879 //        var maxX = pos.right;
30880 //        
30881 //        var maxHeight = 0;
30882 //        
30883 //        Roo.each(items, function(item, k){
30884 //            
30885 //            var c = k % 2;
30886 //            
30887 //            item.el.position('absolute');
30888 //                
30889 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30890 //
30891 //            item.el.setWidth(width);
30892 //
30893 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30894 //
30895 //            item.el.setHeight(height);
30896 //            
30897 //            if(c == 0){
30898 //                item.el.setXY([x, y], isInstant ? false : true);
30899 //            } else {
30900 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30901 //            }
30902 //            
30903 //            y = y + height + this.alternativePadWidth;
30904 //            
30905 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30906 //            
30907 //        }, this);
30908 //        
30909 //        this.el.setHeight(maxHeight);
30910 //        
30911 //    },
30912     
30913     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30914     {
30915         var pos = this.el.getBox(true);
30916         
30917         var minX = pos.x;
30918         var minY = pos.y;
30919         
30920         var maxX = pos.right;
30921         
30922         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30923         
30924         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30925         
30926         Roo.each(queue, function(box, k){
30927             
30928             Roo.each(box, function(b, kk){
30929                 
30930                 b.el.position('absolute');
30931                 
30932                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30933                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30934                 
30935                 if(b.size == 'md-left' || b.size == 'md-right'){
30936                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30937                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30938                 }
30939                 
30940                 b.el.setWidth(width);
30941                 b.el.setHeight(height);
30942                 
30943             }, this);
30944             
30945             if(!box.length){
30946                 return;
30947             }
30948             
30949             var positions = [];
30950             
30951             switch (box.length){
30952                 case 1 :
30953                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30954                     break;
30955                 case 2 :
30956                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30957                     break;
30958                 case 3 :
30959                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30960                     break;
30961                 case 4 :
30962                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30963                     break;
30964                 default :
30965                     break;
30966             }
30967             
30968             Roo.each(box, function(b,kk){
30969                 
30970                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30971                 
30972                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30973                 
30974             }, this);
30975             
30976         }, this);
30977         
30978     },
30979     
30980     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30981     {
30982         Roo.each(eItems, function(b,k){
30983             
30984             b.size = (k == 0) ? 'sm' : 'xs';
30985             b.x = (k == 0) ? 2 : 1;
30986             b.y = (k == 0) ? 2 : 1;
30987             
30988             b.el.position('absolute');
30989             
30990             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30991                 
30992             b.el.setWidth(width);
30993             
30994             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30995             
30996             b.el.setHeight(height);
30997             
30998         }, this);
30999
31000         var positions = [];
31001         
31002         positions.push({
31003             x : maxX - this.unitWidth * 2 - this.gutter,
31004             y : minY
31005         });
31006         
31007         positions.push({
31008             x : maxX - this.unitWidth,
31009             y : minY + (this.unitWidth + this.gutter) * 2
31010         });
31011         
31012         positions.push({
31013             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31014             y : minY
31015         });
31016         
31017         Roo.each(eItems, function(b,k){
31018             
31019             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31020
31021         }, this);
31022         
31023     },
31024     
31025     getVerticalOneBoxColPositions : function(x, y, box)
31026     {
31027         var pos = [];
31028         
31029         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31030         
31031         if(box[0].size == 'md-left'){
31032             rand = 0;
31033         }
31034         
31035         if(box[0].size == 'md-right'){
31036             rand = 1;
31037         }
31038         
31039         pos.push({
31040             x : x + (this.unitWidth + this.gutter) * rand,
31041             y : y
31042         });
31043         
31044         return pos;
31045     },
31046     
31047     getVerticalTwoBoxColPositions : function(x, y, box)
31048     {
31049         var pos = [];
31050         
31051         if(box[0].size == 'xs'){
31052             
31053             pos.push({
31054                 x : x,
31055                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31056             });
31057
31058             pos.push({
31059                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31060                 y : y
31061             });
31062             
31063             return pos;
31064             
31065         }
31066         
31067         pos.push({
31068             x : x,
31069             y : y
31070         });
31071
31072         pos.push({
31073             x : x + (this.unitWidth + this.gutter) * 2,
31074             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31075         });
31076         
31077         return pos;
31078         
31079     },
31080     
31081     getVerticalThreeBoxColPositions : function(x, y, box)
31082     {
31083         var pos = [];
31084         
31085         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31086             
31087             pos.push({
31088                 x : x,
31089                 y : y
31090             });
31091
31092             pos.push({
31093                 x : x + (this.unitWidth + this.gutter) * 1,
31094                 y : y
31095             });
31096             
31097             pos.push({
31098                 x : x + (this.unitWidth + this.gutter) * 2,
31099                 y : y
31100             });
31101             
31102             return pos;
31103             
31104         }
31105         
31106         if(box[0].size == 'xs' && box[1].size == 'xs'){
31107             
31108             pos.push({
31109                 x : x,
31110                 y : y
31111             });
31112
31113             pos.push({
31114                 x : x,
31115                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31116             });
31117             
31118             pos.push({
31119                 x : x + (this.unitWidth + this.gutter) * 1,
31120                 y : y
31121             });
31122             
31123             return pos;
31124             
31125         }
31126         
31127         pos.push({
31128             x : x,
31129             y : y
31130         });
31131
31132         pos.push({
31133             x : x + (this.unitWidth + this.gutter) * 2,
31134             y : y
31135         });
31136
31137         pos.push({
31138             x : x + (this.unitWidth + this.gutter) * 2,
31139             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31140         });
31141             
31142         return pos;
31143         
31144     },
31145     
31146     getVerticalFourBoxColPositions : function(x, y, box)
31147     {
31148         var pos = [];
31149         
31150         if(box[0].size == 'xs'){
31151             
31152             pos.push({
31153                 x : x,
31154                 y : y
31155             });
31156
31157             pos.push({
31158                 x : x,
31159                 y : y + (this.unitHeight + this.gutter) * 1
31160             });
31161             
31162             pos.push({
31163                 x : x,
31164                 y : y + (this.unitHeight + this.gutter) * 2
31165             });
31166             
31167             pos.push({
31168                 x : x + (this.unitWidth + this.gutter) * 1,
31169                 y : y
31170             });
31171             
31172             return pos;
31173             
31174         }
31175         
31176         pos.push({
31177             x : x,
31178             y : y
31179         });
31180
31181         pos.push({
31182             x : x + (this.unitWidth + this.gutter) * 2,
31183             y : y
31184         });
31185
31186         pos.push({
31187             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31188             y : y + (this.unitHeight + this.gutter) * 1
31189         });
31190
31191         pos.push({
31192             x : x + (this.unitWidth + this.gutter) * 2,
31193             y : y + (this.unitWidth + this.gutter) * 2
31194         });
31195
31196         return pos;
31197         
31198     },
31199     
31200     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31201     {
31202         var pos = [];
31203         
31204         if(box[0].size == 'md-left'){
31205             pos.push({
31206                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31207                 y : minY
31208             });
31209             
31210             return pos;
31211         }
31212         
31213         if(box[0].size == 'md-right'){
31214             pos.push({
31215                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31216                 y : minY + (this.unitWidth + this.gutter) * 1
31217             });
31218             
31219             return pos;
31220         }
31221         
31222         var rand = Math.floor(Math.random() * (4 - box[0].y));
31223         
31224         pos.push({
31225             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31226             y : minY + (this.unitWidth + this.gutter) * rand
31227         });
31228         
31229         return pos;
31230         
31231     },
31232     
31233     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31234     {
31235         var pos = [];
31236         
31237         if(box[0].size == 'xs'){
31238             
31239             pos.push({
31240                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31241                 y : minY
31242             });
31243
31244             pos.push({
31245                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31246                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31247             });
31248             
31249             return pos;
31250             
31251         }
31252         
31253         pos.push({
31254             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31255             y : minY
31256         });
31257
31258         pos.push({
31259             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31260             y : minY + (this.unitWidth + this.gutter) * 2
31261         });
31262         
31263         return pos;
31264         
31265     },
31266     
31267     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31268     {
31269         var pos = [];
31270         
31271         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31272             
31273             pos.push({
31274                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31275                 y : minY
31276             });
31277
31278             pos.push({
31279                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31280                 y : minY + (this.unitWidth + this.gutter) * 1
31281             });
31282             
31283             pos.push({
31284                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31285                 y : minY + (this.unitWidth + this.gutter) * 2
31286             });
31287             
31288             return pos;
31289             
31290         }
31291         
31292         if(box[0].size == 'xs' && box[1].size == 'xs'){
31293             
31294             pos.push({
31295                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31296                 y : minY
31297             });
31298
31299             pos.push({
31300                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31301                 y : minY
31302             });
31303             
31304             pos.push({
31305                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31306                 y : minY + (this.unitWidth + this.gutter) * 1
31307             });
31308             
31309             return pos;
31310             
31311         }
31312         
31313         pos.push({
31314             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31315             y : minY
31316         });
31317
31318         pos.push({
31319             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31320             y : minY + (this.unitWidth + this.gutter) * 2
31321         });
31322
31323         pos.push({
31324             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31325             y : minY + (this.unitWidth + this.gutter) * 2
31326         });
31327             
31328         return pos;
31329         
31330     },
31331     
31332     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31333     {
31334         var pos = [];
31335         
31336         if(box[0].size == 'xs'){
31337             
31338             pos.push({
31339                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31340                 y : minY
31341             });
31342
31343             pos.push({
31344                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31345                 y : minY
31346             });
31347             
31348             pos.push({
31349                 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),
31350                 y : minY
31351             });
31352             
31353             pos.push({
31354                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31355                 y : minY + (this.unitWidth + this.gutter) * 1
31356             });
31357             
31358             return pos;
31359             
31360         }
31361         
31362         pos.push({
31363             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31364             y : minY
31365         });
31366         
31367         pos.push({
31368             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31369             y : minY + (this.unitWidth + this.gutter) * 2
31370         });
31371         
31372         pos.push({
31373             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31374             y : minY + (this.unitWidth + this.gutter) * 2
31375         });
31376         
31377         pos.push({
31378             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),
31379             y : minY + (this.unitWidth + this.gutter) * 2
31380         });
31381
31382         return pos;
31383         
31384     },
31385     
31386     /**
31387     * remove a Masonry Brick
31388     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31389     */
31390     removeBrick : function(brick_id)
31391     {
31392         if (!brick_id) {
31393             return;
31394         }
31395         
31396         for (var i = 0; i<this.bricks.length; i++) {
31397             if (this.bricks[i].id == brick_id) {
31398                 this.bricks.splice(i,1);
31399                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31400                 this.initial();
31401             }
31402         }
31403     },
31404     
31405     /**
31406     * adds a Masonry Brick
31407     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31408     */
31409     addBrick : function(cfg)
31410     {
31411         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31412         //this.register(cn);
31413         cn.parentId = this.id;
31414         cn.onRender(this.el, null);
31415         return cn;
31416     },
31417     
31418     /**
31419     * register a Masonry Brick
31420     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31421     */
31422     
31423     register : function(brick)
31424     {
31425         this.bricks.push(brick);
31426         brick.masonryId = this.id;
31427     },
31428     
31429     /**
31430     * clear all the Masonry Brick
31431     */
31432     clearAll : function()
31433     {
31434         this.bricks = [];
31435         //this.getChildContainer().dom.innerHTML = "";
31436         this.el.dom.innerHTML = '';
31437     },
31438     
31439     getSelected : function()
31440     {
31441         if (!this.selectedBrick) {
31442             return false;
31443         }
31444         
31445         return this.selectedBrick;
31446     }
31447 });
31448
31449 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31450     
31451     groups: {},
31452      /**
31453     * register a Masonry Layout
31454     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31455     */
31456     
31457     register : function(layout)
31458     {
31459         this.groups[layout.id] = layout;
31460     },
31461     /**
31462     * fetch a  Masonry Layout based on the masonry layout ID
31463     * @param {string} the masonry layout to add
31464     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31465     */
31466     
31467     get: function(layout_id) {
31468         if (typeof(this.groups[layout_id]) == 'undefined') {
31469             return false;
31470         }
31471         return this.groups[layout_id] ;
31472     }
31473     
31474     
31475     
31476 });
31477
31478  
31479
31480  /**
31481  *
31482  * This is based on 
31483  * http://masonry.desandro.com
31484  *
31485  * The idea is to render all the bricks based on vertical width...
31486  *
31487  * The original code extends 'outlayer' - we might need to use that....
31488  * 
31489  */
31490
31491
31492 /**
31493  * @class Roo.bootstrap.LayoutMasonryAuto
31494  * @extends Roo.bootstrap.Component
31495  * Bootstrap Layout Masonry class
31496  * 
31497  * @constructor
31498  * Create a new Element
31499  * @param {Object} config The config object
31500  */
31501
31502 Roo.bootstrap.LayoutMasonryAuto = function(config){
31503     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31504 };
31505
31506 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31507     
31508       /**
31509      * @cfg {Boolean} isFitWidth  - resize the width..
31510      */   
31511     isFitWidth : false,  // options..
31512     /**
31513      * @cfg {Boolean} isOriginLeft = left align?
31514      */   
31515     isOriginLeft : true,
31516     /**
31517      * @cfg {Boolean} isOriginTop = top align?
31518      */   
31519     isOriginTop : false,
31520     /**
31521      * @cfg {Boolean} isLayoutInstant = no animation?
31522      */   
31523     isLayoutInstant : false, // needed?
31524     /**
31525      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31526      */   
31527     isResizingContainer : true,
31528     /**
31529      * @cfg {Number} columnWidth  width of the columns 
31530      */   
31531     
31532     columnWidth : 0,
31533     
31534     /**
31535      * @cfg {Number} maxCols maximum number of columns
31536      */   
31537     
31538     maxCols: 0,
31539     /**
31540      * @cfg {Number} padHeight padding below box..
31541      */   
31542     
31543     padHeight : 10, 
31544     
31545     /**
31546      * @cfg {Boolean} isAutoInitial defalut true
31547      */   
31548     
31549     isAutoInitial : true, 
31550     
31551     // private?
31552     gutter : 0,
31553     
31554     containerWidth: 0,
31555     initialColumnWidth : 0,
31556     currentSize : null,
31557     
31558     colYs : null, // array.
31559     maxY : 0,
31560     padWidth: 10,
31561     
31562     
31563     tag: 'div',
31564     cls: '',
31565     bricks: null, //CompositeElement
31566     cols : 0, // array?
31567     // element : null, // wrapped now this.el
31568     _isLayoutInited : null, 
31569     
31570     
31571     getAutoCreate : function(){
31572         
31573         var cfg = {
31574             tag: this.tag,
31575             cls: 'blog-masonary-wrapper ' + this.cls,
31576             cn : {
31577                 cls : 'mas-boxes masonary'
31578             }
31579         };
31580         
31581         return cfg;
31582     },
31583     
31584     getChildContainer: function( )
31585     {
31586         if (this.boxesEl) {
31587             return this.boxesEl;
31588         }
31589         
31590         this.boxesEl = this.el.select('.mas-boxes').first();
31591         
31592         return this.boxesEl;
31593     },
31594     
31595     
31596     initEvents : function()
31597     {
31598         var _this = this;
31599         
31600         if(this.isAutoInitial){
31601             Roo.log('hook children rendered');
31602             this.on('childrenrendered', function() {
31603                 Roo.log('children rendered');
31604                 _this.initial();
31605             } ,this);
31606         }
31607         
31608     },
31609     
31610     initial : function()
31611     {
31612         this.reloadItems();
31613
31614         this.currentSize = this.el.getBox(true);
31615
31616         /// was window resize... - let's see if this works..
31617         Roo.EventManager.onWindowResize(this.resize, this); 
31618
31619         if(!this.isAutoInitial){
31620             this.layout();
31621             return;
31622         }
31623         
31624         this.layout.defer(500,this);
31625     },
31626     
31627     reloadItems: function()
31628     {
31629         this.bricks = this.el.select('.masonry-brick', true);
31630         
31631         this.bricks.each(function(b) {
31632             //Roo.log(b.getSize());
31633             if (!b.attr('originalwidth')) {
31634                 b.attr('originalwidth',  b.getSize().width);
31635             }
31636             
31637         });
31638         
31639         Roo.log(this.bricks.elements.length);
31640     },
31641     
31642     resize : function()
31643     {
31644         Roo.log('resize');
31645         var cs = this.el.getBox(true);
31646         
31647         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31648             Roo.log("no change in with or X");
31649             return;
31650         }
31651         this.currentSize = cs;
31652         this.layout();
31653     },
31654     
31655     layout : function()
31656     {
31657          Roo.log('layout');
31658         this._resetLayout();
31659         //this._manageStamps();
31660       
31661         // don't animate first layout
31662         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31663         this.layoutItems( isInstant );
31664       
31665         // flag for initalized
31666         this._isLayoutInited = true;
31667     },
31668     
31669     layoutItems : function( isInstant )
31670     {
31671         //var items = this._getItemsForLayout( this.items );
31672         // original code supports filtering layout items.. we just ignore it..
31673         
31674         this._layoutItems( this.bricks , isInstant );
31675       
31676         this._postLayout();
31677     },
31678     _layoutItems : function ( items , isInstant)
31679     {
31680        //this.fireEvent( 'layout', this, items );
31681     
31682
31683         if ( !items || !items.elements.length ) {
31684           // no items, emit event with empty array
31685             return;
31686         }
31687
31688         var queue = [];
31689         items.each(function(item) {
31690             Roo.log("layout item");
31691             Roo.log(item);
31692             // get x/y object from method
31693             var position = this._getItemLayoutPosition( item );
31694             // enqueue
31695             position.item = item;
31696             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31697             queue.push( position );
31698         }, this);
31699       
31700         this._processLayoutQueue( queue );
31701     },
31702     /** Sets position of item in DOM
31703     * @param {Element} item
31704     * @param {Number} x - horizontal position
31705     * @param {Number} y - vertical position
31706     * @param {Boolean} isInstant - disables transitions
31707     */
31708     _processLayoutQueue : function( queue )
31709     {
31710         for ( var i=0, len = queue.length; i < len; i++ ) {
31711             var obj = queue[i];
31712             obj.item.position('absolute');
31713             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31714         }
31715     },
31716       
31717     
31718     /**
31719     * Any logic you want to do after each layout,
31720     * i.e. size the container
31721     */
31722     _postLayout : function()
31723     {
31724         this.resizeContainer();
31725     },
31726     
31727     resizeContainer : function()
31728     {
31729         if ( !this.isResizingContainer ) {
31730             return;
31731         }
31732         var size = this._getContainerSize();
31733         if ( size ) {
31734             this.el.setSize(size.width,size.height);
31735             this.boxesEl.setSize(size.width,size.height);
31736         }
31737     },
31738     
31739     
31740     
31741     _resetLayout : function()
31742     {
31743         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31744         this.colWidth = this.el.getWidth();
31745         //this.gutter = this.el.getWidth(); 
31746         
31747         this.measureColumns();
31748
31749         // reset column Y
31750         var i = this.cols;
31751         this.colYs = [];
31752         while (i--) {
31753             this.colYs.push( 0 );
31754         }
31755     
31756         this.maxY = 0;
31757     },
31758
31759     measureColumns : function()
31760     {
31761         this.getContainerWidth();
31762       // if columnWidth is 0, default to outerWidth of first item
31763         if ( !this.columnWidth ) {
31764             var firstItem = this.bricks.first();
31765             Roo.log(firstItem);
31766             this.columnWidth  = this.containerWidth;
31767             if (firstItem && firstItem.attr('originalwidth') ) {
31768                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31769             }
31770             // columnWidth fall back to item of first element
31771             Roo.log("set column width?");
31772                         this.initialColumnWidth = this.columnWidth  ;
31773
31774             // if first elem has no width, default to size of container
31775             
31776         }
31777         
31778         
31779         if (this.initialColumnWidth) {
31780             this.columnWidth = this.initialColumnWidth;
31781         }
31782         
31783         
31784             
31785         // column width is fixed at the top - however if container width get's smaller we should
31786         // reduce it...
31787         
31788         // this bit calcs how man columns..
31789             
31790         var columnWidth = this.columnWidth += this.gutter;
31791       
31792         // calculate columns
31793         var containerWidth = this.containerWidth + this.gutter;
31794         
31795         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31796         // fix rounding errors, typically with gutters
31797         var excess = columnWidth - containerWidth % columnWidth;
31798         
31799         
31800         // if overshoot is less than a pixel, round up, otherwise floor it
31801         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31802         cols = Math[ mathMethod ]( cols );
31803         this.cols = Math.max( cols, 1 );
31804         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31805         
31806          // padding positioning..
31807         var totalColWidth = this.cols * this.columnWidth;
31808         var padavail = this.containerWidth - totalColWidth;
31809         // so for 2 columns - we need 3 'pads'
31810         
31811         var padNeeded = (1+this.cols) * this.padWidth;
31812         
31813         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31814         
31815         this.columnWidth += padExtra
31816         //this.padWidth = Math.floor(padavail /  ( this.cols));
31817         
31818         // adjust colum width so that padding is fixed??
31819         
31820         // we have 3 columns ... total = width * 3
31821         // we have X left over... that should be used by 
31822         
31823         //if (this.expandC) {
31824             
31825         //}
31826         
31827         
31828         
31829     },
31830     
31831     getContainerWidth : function()
31832     {
31833        /* // container is parent if fit width
31834         var container = this.isFitWidth ? this.element.parentNode : this.element;
31835         // check that this.size and size are there
31836         // IE8 triggers resize on body size change, so they might not be
31837         
31838         var size = getSize( container );  //FIXME
31839         this.containerWidth = size && size.innerWidth; //FIXME
31840         */
31841          
31842         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31843         
31844     },
31845     
31846     _getItemLayoutPosition : function( item )  // what is item?
31847     {
31848         // we resize the item to our columnWidth..
31849       
31850         item.setWidth(this.columnWidth);
31851         item.autoBoxAdjust  = false;
31852         
31853         var sz = item.getSize();
31854  
31855         // how many columns does this brick span
31856         var remainder = this.containerWidth % this.columnWidth;
31857         
31858         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31859         // round if off by 1 pixel, otherwise use ceil
31860         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31861         colSpan = Math.min( colSpan, this.cols );
31862         
31863         // normally this should be '1' as we dont' currently allow multi width columns..
31864         
31865         var colGroup = this._getColGroup( colSpan );
31866         // get the minimum Y value from the columns
31867         var minimumY = Math.min.apply( Math, colGroup );
31868         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31869         
31870         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31871          
31872         // position the brick
31873         var position = {
31874             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31875             y: this.currentSize.y + minimumY + this.padHeight
31876         };
31877         
31878         Roo.log(position);
31879         // apply setHeight to necessary columns
31880         var setHeight = minimumY + sz.height + this.padHeight;
31881         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31882         
31883         var setSpan = this.cols + 1 - colGroup.length;
31884         for ( var i = 0; i < setSpan; i++ ) {
31885           this.colYs[ shortColIndex + i ] = setHeight ;
31886         }
31887       
31888         return position;
31889     },
31890     
31891     /**
31892      * @param {Number} colSpan - number of columns the element spans
31893      * @returns {Array} colGroup
31894      */
31895     _getColGroup : function( colSpan )
31896     {
31897         if ( colSpan < 2 ) {
31898           // if brick spans only one column, use all the column Ys
31899           return this.colYs;
31900         }
31901       
31902         var colGroup = [];
31903         // how many different places could this brick fit horizontally
31904         var groupCount = this.cols + 1 - colSpan;
31905         // for each group potential horizontal position
31906         for ( var i = 0; i < groupCount; i++ ) {
31907           // make an array of colY values for that one group
31908           var groupColYs = this.colYs.slice( i, i + colSpan );
31909           // and get the max value of the array
31910           colGroup[i] = Math.max.apply( Math, groupColYs );
31911         }
31912         return colGroup;
31913     },
31914     /*
31915     _manageStamp : function( stamp )
31916     {
31917         var stampSize =  stamp.getSize();
31918         var offset = stamp.getBox();
31919         // get the columns that this stamp affects
31920         var firstX = this.isOriginLeft ? offset.x : offset.right;
31921         var lastX = firstX + stampSize.width;
31922         var firstCol = Math.floor( firstX / this.columnWidth );
31923         firstCol = Math.max( 0, firstCol );
31924         
31925         var lastCol = Math.floor( lastX / this.columnWidth );
31926         // lastCol should not go over if multiple of columnWidth #425
31927         lastCol -= lastX % this.columnWidth ? 0 : 1;
31928         lastCol = Math.min( this.cols - 1, lastCol );
31929         
31930         // set colYs to bottom of the stamp
31931         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31932             stampSize.height;
31933             
31934         for ( var i = firstCol; i <= lastCol; i++ ) {
31935           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31936         }
31937     },
31938     */
31939     
31940     _getContainerSize : function()
31941     {
31942         this.maxY = Math.max.apply( Math, this.colYs );
31943         var size = {
31944             height: this.maxY
31945         };
31946       
31947         if ( this.isFitWidth ) {
31948             size.width = this._getContainerFitWidth();
31949         }
31950       
31951         return size;
31952     },
31953     
31954     _getContainerFitWidth : function()
31955     {
31956         var unusedCols = 0;
31957         // count unused columns
31958         var i = this.cols;
31959         while ( --i ) {
31960           if ( this.colYs[i] !== 0 ) {
31961             break;
31962           }
31963           unusedCols++;
31964         }
31965         // fit container to columns that have been used
31966         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31967     },
31968     
31969     needsResizeLayout : function()
31970     {
31971         var previousWidth = this.containerWidth;
31972         this.getContainerWidth();
31973         return previousWidth !== this.containerWidth;
31974     }
31975  
31976 });
31977
31978  
31979
31980  /*
31981  * - LGPL
31982  *
31983  * element
31984  * 
31985  */
31986
31987 /**
31988  * @class Roo.bootstrap.MasonryBrick
31989  * @extends Roo.bootstrap.Component
31990  * Bootstrap MasonryBrick class
31991  * 
31992  * @constructor
31993  * Create a new MasonryBrick
31994  * @param {Object} config The config object
31995  */
31996
31997 Roo.bootstrap.MasonryBrick = function(config){
31998     
31999     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32000     
32001     Roo.bootstrap.MasonryBrick.register(this);
32002     
32003     this.addEvents({
32004         // raw events
32005         /**
32006          * @event click
32007          * When a MasonryBrick is clcik
32008          * @param {Roo.bootstrap.MasonryBrick} this
32009          * @param {Roo.EventObject} e
32010          */
32011         "click" : true
32012     });
32013 };
32014
32015 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32016     
32017     /**
32018      * @cfg {String} title
32019      */   
32020     title : '',
32021     /**
32022      * @cfg {String} html
32023      */   
32024     html : '',
32025     /**
32026      * @cfg {String} bgimage
32027      */   
32028     bgimage : '',
32029     /**
32030      * @cfg {String} videourl
32031      */   
32032     videourl : '',
32033     /**
32034      * @cfg {String} cls
32035      */   
32036     cls : '',
32037     /**
32038      * @cfg {String} href
32039      */   
32040     href : '',
32041     /**
32042      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32043      */   
32044     size : 'xs',
32045     
32046     /**
32047      * @cfg {String} placetitle (center|bottom)
32048      */   
32049     placetitle : '',
32050     
32051     /**
32052      * @cfg {Boolean} isFitContainer defalut true
32053      */   
32054     isFitContainer : true, 
32055     
32056     /**
32057      * @cfg {Boolean} preventDefault defalut false
32058      */   
32059     preventDefault : false, 
32060     
32061     /**
32062      * @cfg {Boolean} inverse defalut false
32063      */   
32064     maskInverse : false, 
32065     
32066     getAutoCreate : function()
32067     {
32068         if(!this.isFitContainer){
32069             return this.getSplitAutoCreate();
32070         }
32071         
32072         var cls = 'masonry-brick masonry-brick-full';
32073         
32074         if(this.href.length){
32075             cls += ' masonry-brick-link';
32076         }
32077         
32078         if(this.bgimage.length){
32079             cls += ' masonry-brick-image';
32080         }
32081         
32082         if(this.maskInverse){
32083             cls += ' mask-inverse';
32084         }
32085         
32086         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32087             cls += ' enable-mask';
32088         }
32089         
32090         if(this.size){
32091             cls += ' masonry-' + this.size + '-brick';
32092         }
32093         
32094         if(this.placetitle.length){
32095             
32096             switch (this.placetitle) {
32097                 case 'center' :
32098                     cls += ' masonry-center-title';
32099                     break;
32100                 case 'bottom' :
32101                     cls += ' masonry-bottom-title';
32102                     break;
32103                 default:
32104                     break;
32105             }
32106             
32107         } else {
32108             if(!this.html.length && !this.bgimage.length){
32109                 cls += ' masonry-center-title';
32110             }
32111
32112             if(!this.html.length && this.bgimage.length){
32113                 cls += ' masonry-bottom-title';
32114             }
32115         }
32116         
32117         if(this.cls){
32118             cls += ' ' + this.cls;
32119         }
32120         
32121         var cfg = {
32122             tag: (this.href.length) ? 'a' : 'div',
32123             cls: cls,
32124             cn: [
32125                 {
32126                     tag: 'div',
32127                     cls: 'masonry-brick-mask'
32128                 },
32129                 {
32130                     tag: 'div',
32131                     cls: 'masonry-brick-paragraph',
32132                     cn: []
32133                 }
32134             ]
32135         };
32136         
32137         if(this.href.length){
32138             cfg.href = this.href;
32139         }
32140         
32141         var cn = cfg.cn[1].cn;
32142         
32143         if(this.title.length){
32144             cn.push({
32145                 tag: 'h4',
32146                 cls: 'masonry-brick-title',
32147                 html: this.title
32148             });
32149         }
32150         
32151         if(this.html.length){
32152             cn.push({
32153                 tag: 'p',
32154                 cls: 'masonry-brick-text',
32155                 html: this.html
32156             });
32157         }
32158         
32159         if (!this.title.length && !this.html.length) {
32160             cfg.cn[1].cls += ' hide';
32161         }
32162         
32163         if(this.bgimage.length){
32164             cfg.cn.push({
32165                 tag: 'img',
32166                 cls: 'masonry-brick-image-view',
32167                 src: this.bgimage
32168             });
32169         }
32170         
32171         if(this.videourl.length){
32172             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32173             // youtube support only?
32174             cfg.cn.push({
32175                 tag: 'iframe',
32176                 cls: 'masonry-brick-image-view',
32177                 src: vurl,
32178                 frameborder : 0,
32179                 allowfullscreen : true
32180             });
32181         }
32182         
32183         return cfg;
32184         
32185     },
32186     
32187     getSplitAutoCreate : function()
32188     {
32189         var cls = 'masonry-brick masonry-brick-split';
32190         
32191         if(this.href.length){
32192             cls += ' masonry-brick-link';
32193         }
32194         
32195         if(this.bgimage.length){
32196             cls += ' masonry-brick-image';
32197         }
32198         
32199         if(this.size){
32200             cls += ' masonry-' + this.size + '-brick';
32201         }
32202         
32203         switch (this.placetitle) {
32204             case 'center' :
32205                 cls += ' masonry-center-title';
32206                 break;
32207             case 'bottom' :
32208                 cls += ' masonry-bottom-title';
32209                 break;
32210             default:
32211                 if(!this.bgimage.length){
32212                     cls += ' masonry-center-title';
32213                 }
32214
32215                 if(this.bgimage.length){
32216                     cls += ' masonry-bottom-title';
32217                 }
32218                 break;
32219         }
32220         
32221         if(this.cls){
32222             cls += ' ' + this.cls;
32223         }
32224         
32225         var cfg = {
32226             tag: (this.href.length) ? 'a' : 'div',
32227             cls: cls,
32228             cn: [
32229                 {
32230                     tag: 'div',
32231                     cls: 'masonry-brick-split-head',
32232                     cn: [
32233                         {
32234                             tag: 'div',
32235                             cls: 'masonry-brick-paragraph',
32236                             cn: []
32237                         }
32238                     ]
32239                 },
32240                 {
32241                     tag: 'div',
32242                     cls: 'masonry-brick-split-body',
32243                     cn: []
32244                 }
32245             ]
32246         };
32247         
32248         if(this.href.length){
32249             cfg.href = this.href;
32250         }
32251         
32252         if(this.title.length){
32253             cfg.cn[0].cn[0].cn.push({
32254                 tag: 'h4',
32255                 cls: 'masonry-brick-title',
32256                 html: this.title
32257             });
32258         }
32259         
32260         if(this.html.length){
32261             cfg.cn[1].cn.push({
32262                 tag: 'p',
32263                 cls: 'masonry-brick-text',
32264                 html: this.html
32265             });
32266         }
32267
32268         if(this.bgimage.length){
32269             cfg.cn[0].cn.push({
32270                 tag: 'img',
32271                 cls: 'masonry-brick-image-view',
32272                 src: this.bgimage
32273             });
32274         }
32275         
32276         if(this.videourl.length){
32277             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32278             // youtube support only?
32279             cfg.cn[0].cn.cn.push({
32280                 tag: 'iframe',
32281                 cls: 'masonry-brick-image-view',
32282                 src: vurl,
32283                 frameborder : 0,
32284                 allowfullscreen : true
32285             });
32286         }
32287         
32288         return cfg;
32289     },
32290     
32291     initEvents: function() 
32292     {
32293         switch (this.size) {
32294             case 'xs' :
32295                 this.x = 1;
32296                 this.y = 1;
32297                 break;
32298             case 'sm' :
32299                 this.x = 2;
32300                 this.y = 2;
32301                 break;
32302             case 'md' :
32303             case 'md-left' :
32304             case 'md-right' :
32305                 this.x = 3;
32306                 this.y = 3;
32307                 break;
32308             case 'tall' :
32309                 this.x = 2;
32310                 this.y = 3;
32311                 break;
32312             case 'wide' :
32313                 this.x = 3;
32314                 this.y = 2;
32315                 break;
32316             case 'wide-thin' :
32317                 this.x = 3;
32318                 this.y = 1;
32319                 break;
32320                         
32321             default :
32322                 break;
32323         }
32324         
32325         if(Roo.isTouch){
32326             this.el.on('touchstart', this.onTouchStart, this);
32327             this.el.on('touchmove', this.onTouchMove, this);
32328             this.el.on('touchend', this.onTouchEnd, this);
32329             this.el.on('contextmenu', this.onContextMenu, this);
32330         } else {
32331             this.el.on('mouseenter'  ,this.enter, this);
32332             this.el.on('mouseleave', this.leave, this);
32333             this.el.on('click', this.onClick, this);
32334         }
32335         
32336         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32337             this.parent().bricks.push(this);   
32338         }
32339         
32340     },
32341     
32342     onClick: function(e, el)
32343     {
32344         var time = this.endTimer - this.startTimer;
32345         // Roo.log(e.preventDefault());
32346         if(Roo.isTouch){
32347             if(time > 1000){
32348                 e.preventDefault();
32349                 return;
32350             }
32351         }
32352         
32353         if(!this.preventDefault){
32354             return;
32355         }
32356         
32357         e.preventDefault();
32358         
32359         if (this.activcClass != '') {
32360             this.selectBrick();
32361         }
32362         
32363         this.fireEvent('click', this);
32364     },
32365     
32366     enter: function(e, el)
32367     {
32368         e.preventDefault();
32369         
32370         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32371             return;
32372         }
32373         
32374         if(this.bgimage.length && this.html.length){
32375             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32376         }
32377     },
32378     
32379     leave: function(e, el)
32380     {
32381         e.preventDefault();
32382         
32383         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32384             return;
32385         }
32386         
32387         if(this.bgimage.length && this.html.length){
32388             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32389         }
32390     },
32391     
32392     onTouchStart: function(e, el)
32393     {
32394 //        e.preventDefault();
32395         
32396         this.touchmoved = false;
32397         
32398         if(!this.isFitContainer){
32399             return;
32400         }
32401         
32402         if(!this.bgimage.length || !this.html.length){
32403             return;
32404         }
32405         
32406         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32407         
32408         this.timer = new Date().getTime();
32409         
32410     },
32411     
32412     onTouchMove: function(e, el)
32413     {
32414         this.touchmoved = true;
32415     },
32416     
32417     onContextMenu : function(e,el)
32418     {
32419         e.preventDefault();
32420         e.stopPropagation();
32421         return false;
32422     },
32423     
32424     onTouchEnd: function(e, el)
32425     {
32426 //        e.preventDefault();
32427         
32428         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32429         
32430             this.leave(e,el);
32431             
32432             return;
32433         }
32434         
32435         if(!this.bgimage.length || !this.html.length){
32436             
32437             if(this.href.length){
32438                 window.location.href = this.href;
32439             }
32440             
32441             return;
32442         }
32443         
32444         if(!this.isFitContainer){
32445             return;
32446         }
32447         
32448         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32449         
32450         window.location.href = this.href;
32451     },
32452     
32453     //selection on single brick only
32454     selectBrick : function() {
32455         
32456         if (!this.parentId) {
32457             return;
32458         }
32459         
32460         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32461         var index = m.selectedBrick.indexOf(this.id);
32462         
32463         if ( index > -1) {
32464             m.selectedBrick.splice(index,1);
32465             this.el.removeClass(this.activeClass);
32466             return;
32467         }
32468         
32469         for(var i = 0; i < m.selectedBrick.length; i++) {
32470             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32471             b.el.removeClass(b.activeClass);
32472         }
32473         
32474         m.selectedBrick = [];
32475         
32476         m.selectedBrick.push(this.id);
32477         this.el.addClass(this.activeClass);
32478         return;
32479     }
32480     
32481 });
32482
32483 Roo.apply(Roo.bootstrap.MasonryBrick, {
32484     
32485     //groups: {},
32486     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32487      /**
32488     * register a Masonry Brick
32489     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32490     */
32491     
32492     register : function(brick)
32493     {
32494         //this.groups[brick.id] = brick;
32495         this.groups.add(brick.id, brick);
32496     },
32497     /**
32498     * fetch a  masonry brick based on the masonry brick ID
32499     * @param {string} the masonry brick to add
32500     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32501     */
32502     
32503     get: function(brick_id) 
32504     {
32505         // if (typeof(this.groups[brick_id]) == 'undefined') {
32506         //     return false;
32507         // }
32508         // return this.groups[brick_id] ;
32509         
32510         if(this.groups.key(brick_id)) {
32511             return this.groups.key(brick_id);
32512         }
32513         
32514         return false;
32515     }
32516     
32517     
32518     
32519 });
32520
32521  /*
32522  * - LGPL
32523  *
32524  * element
32525  * 
32526  */
32527
32528 /**
32529  * @class Roo.bootstrap.Brick
32530  * @extends Roo.bootstrap.Component
32531  * Bootstrap Brick class
32532  * 
32533  * @constructor
32534  * Create a new Brick
32535  * @param {Object} config The config object
32536  */
32537
32538 Roo.bootstrap.Brick = function(config){
32539     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32540     
32541     this.addEvents({
32542         // raw events
32543         /**
32544          * @event click
32545          * When a Brick is click
32546          * @param {Roo.bootstrap.Brick} this
32547          * @param {Roo.EventObject} e
32548          */
32549         "click" : true
32550     });
32551 };
32552
32553 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32554     
32555     /**
32556      * @cfg {String} title
32557      */   
32558     title : '',
32559     /**
32560      * @cfg {String} html
32561      */   
32562     html : '',
32563     /**
32564      * @cfg {String} bgimage
32565      */   
32566     bgimage : '',
32567     /**
32568      * @cfg {String} cls
32569      */   
32570     cls : '',
32571     /**
32572      * @cfg {String} href
32573      */   
32574     href : '',
32575     /**
32576      * @cfg {String} video
32577      */   
32578     video : '',
32579     /**
32580      * @cfg {Boolean} square
32581      */   
32582     square : true,
32583     
32584     getAutoCreate : function()
32585     {
32586         var cls = 'roo-brick';
32587         
32588         if(this.href.length){
32589             cls += ' roo-brick-link';
32590         }
32591         
32592         if(this.bgimage.length){
32593             cls += ' roo-brick-image';
32594         }
32595         
32596         if(!this.html.length && !this.bgimage.length){
32597             cls += ' roo-brick-center-title';
32598         }
32599         
32600         if(!this.html.length && this.bgimage.length){
32601             cls += ' roo-brick-bottom-title';
32602         }
32603         
32604         if(this.cls){
32605             cls += ' ' + this.cls;
32606         }
32607         
32608         var cfg = {
32609             tag: (this.href.length) ? 'a' : 'div',
32610             cls: cls,
32611             cn: [
32612                 {
32613                     tag: 'div',
32614                     cls: 'roo-brick-paragraph',
32615                     cn: []
32616                 }
32617             ]
32618         };
32619         
32620         if(this.href.length){
32621             cfg.href = this.href;
32622         }
32623         
32624         var cn = cfg.cn[0].cn;
32625         
32626         if(this.title.length){
32627             cn.push({
32628                 tag: 'h4',
32629                 cls: 'roo-brick-title',
32630                 html: this.title
32631             });
32632         }
32633         
32634         if(this.html.length){
32635             cn.push({
32636                 tag: 'p',
32637                 cls: 'roo-brick-text',
32638                 html: this.html
32639             });
32640         } else {
32641             cn.cls += ' hide';
32642         }
32643         
32644         if(this.bgimage.length){
32645             cfg.cn.push({
32646                 tag: 'img',
32647                 cls: 'roo-brick-image-view',
32648                 src: this.bgimage
32649             });
32650         }
32651         
32652         return cfg;
32653     },
32654     
32655     initEvents: function() 
32656     {
32657         if(this.title.length || this.html.length){
32658             this.el.on('mouseenter'  ,this.enter, this);
32659             this.el.on('mouseleave', this.leave, this);
32660         }
32661         
32662         Roo.EventManager.onWindowResize(this.resize, this); 
32663         
32664         if(this.bgimage.length){
32665             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32666             this.imageEl.on('load', this.onImageLoad, this);
32667             return;
32668         }
32669         
32670         this.resize();
32671     },
32672     
32673     onImageLoad : function()
32674     {
32675         this.resize();
32676     },
32677     
32678     resize : function()
32679     {
32680         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32681         
32682         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32683         
32684         if(this.bgimage.length){
32685             var image = this.el.select('.roo-brick-image-view', true).first();
32686             
32687             image.setWidth(paragraph.getWidth());
32688             
32689             if(this.square){
32690                 image.setHeight(paragraph.getWidth());
32691             }
32692             
32693             this.el.setHeight(image.getHeight());
32694             paragraph.setHeight(image.getHeight());
32695             
32696         }
32697         
32698     },
32699     
32700     enter: function(e, el)
32701     {
32702         e.preventDefault();
32703         
32704         if(this.bgimage.length){
32705             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32706             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32707         }
32708     },
32709     
32710     leave: function(e, el)
32711     {
32712         e.preventDefault();
32713         
32714         if(this.bgimage.length){
32715             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32716             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32717         }
32718     }
32719     
32720 });
32721
32722  
32723
32724  /*
32725  * - LGPL
32726  *
32727  * Input
32728  * 
32729  */
32730
32731 /**
32732  * @class Roo.bootstrap.NumberField
32733  * @extends Roo.bootstrap.Input
32734  * Bootstrap NumberField class
32735  * 
32736  * 
32737  * 
32738  * 
32739  * @constructor
32740  * Create a new NumberField
32741  * @param {Object} config The config object
32742  */
32743
32744 Roo.bootstrap.NumberField = function(config){
32745     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32746 };
32747
32748 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32749     
32750     /**
32751      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32752      */
32753     allowDecimals : true,
32754     /**
32755      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32756      */
32757     decimalSeparator : ".",
32758     /**
32759      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32760      */
32761     decimalPrecision : 2,
32762     /**
32763      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32764      */
32765     allowNegative : true,
32766     /**
32767      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32768      */
32769     minValue : Number.NEGATIVE_INFINITY,
32770     /**
32771      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32772      */
32773     maxValue : Number.MAX_VALUE,
32774     /**
32775      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32776      */
32777     minText : "The minimum value for this field is {0}",
32778     /**
32779      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32780      */
32781     maxText : "The maximum value for this field is {0}",
32782     /**
32783      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32784      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32785      */
32786     nanText : "{0} is not a valid number",
32787     /**
32788      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32789      */
32790     castInt : true,
32791
32792     // private
32793     initEvents : function()
32794     {   
32795         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32796         
32797         var allowed = "0123456789";
32798         
32799         if(this.allowDecimals){
32800             allowed += this.decimalSeparator;
32801         }
32802         
32803         if(this.allowNegative){
32804             allowed += "-";
32805         }
32806         
32807         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32808         
32809         var keyPress = function(e){
32810             
32811             var k = e.getKey();
32812             
32813             var c = e.getCharCode();
32814             
32815             if(
32816                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32817                     allowed.indexOf(String.fromCharCode(c)) === -1
32818             ){
32819                 e.stopEvent();
32820                 return;
32821             }
32822             
32823             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32824                 return;
32825             }
32826             
32827             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32828                 e.stopEvent();
32829             }
32830         };
32831         
32832         this.el.on("keypress", keyPress, this);
32833     },
32834     
32835     validateValue : function(value)
32836     {
32837         
32838         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32839             return false;
32840         }
32841         
32842         var num = this.parseValue(value);
32843         
32844         if(isNaN(num)){
32845             this.markInvalid(String.format(this.nanText, value));
32846             return false;
32847         }
32848         
32849         if(num < this.minValue){
32850             this.markInvalid(String.format(this.minText, this.minValue));
32851             return false;
32852         }
32853         
32854         if(num > this.maxValue){
32855             this.markInvalid(String.format(this.maxText, this.maxValue));
32856             return false;
32857         }
32858         
32859         return true;
32860     },
32861
32862     getValue : function()
32863     {
32864         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32865     },
32866
32867     parseValue : function(value)
32868     {
32869         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32870         return isNaN(value) ? '' : value;
32871     },
32872
32873     fixPrecision : function(value)
32874     {
32875         var nan = isNaN(value);
32876         
32877         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32878             return nan ? '' : value;
32879         }
32880         return parseFloat(value).toFixed(this.decimalPrecision);
32881     },
32882
32883     setValue : function(v)
32884     {
32885         v = this.fixPrecision(v);
32886         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32887     },
32888
32889     decimalPrecisionFcn : function(v)
32890     {
32891         return Math.floor(v);
32892     },
32893
32894     beforeBlur : function()
32895     {
32896         if(!this.castInt){
32897             return;
32898         }
32899         
32900         var v = this.parseValue(this.getRawValue());
32901         if(v){
32902             this.setValue(v);
32903         }
32904     }
32905     
32906 });
32907
32908  
32909
32910 /*
32911 * Licence: LGPL
32912 */
32913
32914 /**
32915  * @class Roo.bootstrap.DocumentSlider
32916  * @extends Roo.bootstrap.Component
32917  * Bootstrap DocumentSlider class
32918  * 
32919  * @constructor
32920  * Create a new DocumentViewer
32921  * @param {Object} config The config object
32922  */
32923
32924 Roo.bootstrap.DocumentSlider = function(config){
32925     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32926     
32927     this.files = [];
32928     
32929     this.addEvents({
32930         /**
32931          * @event initial
32932          * Fire after initEvent
32933          * @param {Roo.bootstrap.DocumentSlider} this
32934          */
32935         "initial" : true,
32936         /**
32937          * @event update
32938          * Fire after update
32939          * @param {Roo.bootstrap.DocumentSlider} this
32940          */
32941         "update" : true,
32942         /**
32943          * @event click
32944          * Fire after click
32945          * @param {Roo.bootstrap.DocumentSlider} this
32946          */
32947         "click" : true
32948     });
32949 };
32950
32951 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32952     
32953     files : false,
32954     
32955     indicator : 0,
32956     
32957     getAutoCreate : function()
32958     {
32959         var cfg = {
32960             tag : 'div',
32961             cls : 'roo-document-slider',
32962             cn : [
32963                 {
32964                     tag : 'div',
32965                     cls : 'roo-document-slider-header',
32966                     cn : [
32967                         {
32968                             tag : 'div',
32969                             cls : 'roo-document-slider-header-title'
32970                         }
32971                     ]
32972                 },
32973                 {
32974                     tag : 'div',
32975                     cls : 'roo-document-slider-body',
32976                     cn : [
32977                         {
32978                             tag : 'div',
32979                             cls : 'roo-document-slider-prev',
32980                             cn : [
32981                                 {
32982                                     tag : 'i',
32983                                     cls : 'fa fa-chevron-left'
32984                                 }
32985                             ]
32986                         },
32987                         {
32988                             tag : 'div',
32989                             cls : 'roo-document-slider-thumb',
32990                             cn : [
32991                                 {
32992                                     tag : 'img',
32993                                     cls : 'roo-document-slider-image'
32994                                 }
32995                             ]
32996                         },
32997                         {
32998                             tag : 'div',
32999                             cls : 'roo-document-slider-next',
33000                             cn : [
33001                                 {
33002                                     tag : 'i',
33003                                     cls : 'fa fa-chevron-right'
33004                                 }
33005                             ]
33006                         }
33007                     ]
33008                 }
33009             ]
33010         };
33011         
33012         return cfg;
33013     },
33014     
33015     initEvents : function()
33016     {
33017         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33018         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33019         
33020         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33021         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33022         
33023         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33024         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33025         
33026         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33027         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33028         
33029         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33030         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33031         
33032         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33033         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33034         
33035         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33036         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33037         
33038         this.thumbEl.on('click', this.onClick, this);
33039         
33040         this.prevIndicator.on('click', this.prev, this);
33041         
33042         this.nextIndicator.on('click', this.next, this);
33043         
33044     },
33045     
33046     initial : function()
33047     {
33048         if(this.files.length){
33049             this.indicator = 1;
33050             this.update()
33051         }
33052         
33053         this.fireEvent('initial', this);
33054     },
33055     
33056     update : function()
33057     {
33058         this.imageEl.attr('src', this.files[this.indicator - 1]);
33059         
33060         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33061         
33062         this.prevIndicator.show();
33063         
33064         if(this.indicator == 1){
33065             this.prevIndicator.hide();
33066         }
33067         
33068         this.nextIndicator.show();
33069         
33070         if(this.indicator == this.files.length){
33071             this.nextIndicator.hide();
33072         }
33073         
33074         this.thumbEl.scrollTo('top');
33075         
33076         this.fireEvent('update', this);
33077     },
33078     
33079     onClick : function(e)
33080     {
33081         e.preventDefault();
33082         
33083         this.fireEvent('click', this);
33084     },
33085     
33086     prev : function(e)
33087     {
33088         e.preventDefault();
33089         
33090         this.indicator = Math.max(1, this.indicator - 1);
33091         
33092         this.update();
33093     },
33094     
33095     next : function(e)
33096     {
33097         e.preventDefault();
33098         
33099         this.indicator = Math.min(this.files.length, this.indicator + 1);
33100         
33101         this.update();
33102     }
33103 });
33104 /*
33105  * - LGPL
33106  *
33107  * RadioSet
33108  *
33109  *
33110  */
33111
33112 /**
33113  * @class Roo.bootstrap.RadioSet
33114  * @extends Roo.bootstrap.Input
33115  * Bootstrap RadioSet class
33116  * @cfg {String} indicatorpos (left|right) default left
33117  * @cfg {Boolean} inline (true|false) inline the element (default true)
33118  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33119  * @constructor
33120  * Create a new RadioSet
33121  * @param {Object} config The config object
33122  */
33123
33124 Roo.bootstrap.RadioSet = function(config){
33125     
33126     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33127     
33128     this.radioes = [];
33129     
33130     Roo.bootstrap.RadioSet.register(this);
33131     
33132     this.addEvents({
33133         /**
33134         * @event check
33135         * Fires when the element is checked or unchecked.
33136         * @param {Roo.bootstrap.RadioSet} this This radio
33137         * @param {Roo.bootstrap.Radio} item The checked item
33138         */
33139        check : true
33140     });
33141     
33142 };
33143
33144 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33145
33146     radioes : false,
33147     
33148     inline : true,
33149     
33150     weight : '',
33151     
33152     indicatorpos : 'left',
33153     
33154     getAutoCreate : function()
33155     {
33156         var label = {
33157             tag : 'label',
33158             cls : 'roo-radio-set-label',
33159             cn : [
33160                 {
33161                     tag : 'span',
33162                     html : this.fieldLabel
33163                 }
33164             ]
33165         };
33166         
33167         if(this.indicatorpos == 'left'){
33168             label.cn.unshift({
33169                 tag : 'i',
33170                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33171                 tooltip : 'This field is required'
33172             });
33173         } else {
33174             label.cn.push({
33175                 tag : 'i',
33176                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33177                 tooltip : 'This field is required'
33178             });
33179         }
33180         
33181         var items = {
33182             tag : 'div',
33183             cls : 'roo-radio-set-items'
33184         };
33185         
33186         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33187         
33188         if (align === 'left' && this.fieldLabel.length) {
33189             
33190             items = {
33191                 cls : "roo-radio-set-right", 
33192                 cn: [
33193                     items
33194                 ]
33195             };
33196             
33197             if(this.labelWidth > 12){
33198                 label.style = "width: " + this.labelWidth + 'px';
33199             }
33200             
33201             if(this.labelWidth < 13 && this.labelmd == 0){
33202                 this.labelmd = this.labelWidth;
33203             }
33204             
33205             if(this.labellg > 0){
33206                 label.cls += ' col-lg-' + this.labellg;
33207                 items.cls += ' col-lg-' + (12 - this.labellg);
33208             }
33209             
33210             if(this.labelmd > 0){
33211                 label.cls += ' col-md-' + this.labelmd;
33212                 items.cls += ' col-md-' + (12 - this.labelmd);
33213             }
33214             
33215             if(this.labelsm > 0){
33216                 label.cls += ' col-sm-' + this.labelsm;
33217                 items.cls += ' col-sm-' + (12 - this.labelsm);
33218             }
33219             
33220             if(this.labelxs > 0){
33221                 label.cls += ' col-xs-' + this.labelxs;
33222                 items.cls += ' col-xs-' + (12 - this.labelxs);
33223             }
33224         }
33225         
33226         var cfg = {
33227             tag : 'div',
33228             cls : 'roo-radio-set',
33229             cn : [
33230                 {
33231                     tag : 'input',
33232                     cls : 'roo-radio-set-input',
33233                     type : 'hidden',
33234                     name : this.name,
33235                     value : this.value ? this.value :  ''
33236                 },
33237                 label,
33238                 items
33239             ]
33240         };
33241         
33242         if(this.weight.length){
33243             cfg.cls += ' roo-radio-' + this.weight;
33244         }
33245         
33246         if(this.inline) {
33247             cfg.cls += ' roo-radio-set-inline';
33248         }
33249         
33250         return cfg;
33251         
33252     },
33253
33254     initEvents : function()
33255     {
33256         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33257         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33258         
33259         if(!this.fieldLabel.length){
33260             this.labelEl.hide();
33261         }
33262         
33263         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33264         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33265         
33266         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33267         this.indicatorEl().hide();
33268         
33269         this.originalValue = this.getValue();
33270         
33271     },
33272     
33273     inputEl: function ()
33274     {
33275         return this.el.select('.roo-radio-set-input', true).first();
33276     },
33277     
33278     getChildContainer : function()
33279     {
33280         return this.itemsEl;
33281     },
33282     
33283     register : function(item)
33284     {
33285         this.radioes.push(item);
33286         
33287     },
33288     
33289     validate : function()
33290     {   
33291         var valid = false;
33292         
33293         Roo.each(this.radioes, function(i){
33294             if(!i.checked){
33295                 return;
33296             }
33297             
33298             valid = true;
33299             return false;
33300         });
33301         
33302         if(this.allowBlank) {
33303             return true;
33304         }
33305         
33306         if(this.disabled || valid){
33307             this.markValid();
33308             return true;
33309         }
33310         
33311         this.markInvalid();
33312         return false;
33313         
33314     },
33315     
33316     markValid : function()
33317     {
33318         if(this.labelEl.isVisible(true)){
33319             this.indicatorEl().hide();
33320         }
33321         
33322         this.el.removeClass([this.invalidClass, this.validClass]);
33323         this.el.addClass(this.validClass);
33324         
33325         this.fireEvent('valid', this);
33326     },
33327     
33328     markInvalid : function(msg)
33329     {
33330         if(this.allowBlank || this.disabled){
33331             return;
33332         }
33333         
33334         if(this.labelEl.isVisible(true)){
33335             this.indicatorEl().show();
33336         }
33337         
33338         this.el.removeClass([this.invalidClass, this.validClass]);
33339         this.el.addClass(this.invalidClass);
33340         
33341         this.fireEvent('invalid', this, msg);
33342         
33343     },
33344     
33345     setValue : function(v, suppressEvent)
33346     {   
33347         this.value = v;
33348         if(this.rendered){
33349             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33350         }
33351         
33352         Roo.each(this.radioes, function(i){
33353             
33354             i.checked = false;
33355             i.el.removeClass('checked');
33356             
33357             if(i.value === v || i.value.toString() === v.toString()){
33358                 i.checked = true;
33359                 i.el.addClass('checked');
33360                 
33361                 if(suppressEvent !== true){
33362                     this.fireEvent('check', this, i);
33363                 }
33364             }
33365             
33366         }, this);
33367         
33368         this.validate();
33369     },
33370     
33371     clearInvalid : function(){
33372         
33373         if(!this.el || this.preventMark){
33374             return;
33375         }
33376         
33377         this.el.removeClass([this.invalidClass]);
33378         
33379         this.fireEvent('valid', this);
33380     }
33381     
33382 });
33383
33384 Roo.apply(Roo.bootstrap.RadioSet, {
33385     
33386     groups: {},
33387     
33388     register : function(set)
33389     {
33390         this.groups[set.name] = set;
33391     },
33392     
33393     get: function(name) 
33394     {
33395         if (typeof(this.groups[name]) == 'undefined') {
33396             return false;
33397         }
33398         
33399         return this.groups[name] ;
33400     }
33401     
33402 });
33403 /*
33404  * Based on:
33405  * Ext JS Library 1.1.1
33406  * Copyright(c) 2006-2007, Ext JS, LLC.
33407  *
33408  * Originally Released Under LGPL - original licence link has changed is not relivant.
33409  *
33410  * Fork - LGPL
33411  * <script type="text/javascript">
33412  */
33413
33414
33415 /**
33416  * @class Roo.bootstrap.SplitBar
33417  * @extends Roo.util.Observable
33418  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33419  * <br><br>
33420  * Usage:
33421  * <pre><code>
33422 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33423                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33424 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33425 split.minSize = 100;
33426 split.maxSize = 600;
33427 split.animate = true;
33428 split.on('moved', splitterMoved);
33429 </code></pre>
33430  * @constructor
33431  * Create a new SplitBar
33432  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33433  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33434  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33435  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33436                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33437                         position of the SplitBar).
33438  */
33439 Roo.bootstrap.SplitBar = function(cfg){
33440     
33441     /** @private */
33442     
33443     //{
33444     //  dragElement : elm
33445     //  resizingElement: el,
33446         // optional..
33447     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33448     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33449         // existingProxy ???
33450     //}
33451     
33452     this.el = Roo.get(cfg.dragElement, true);
33453     this.el.dom.unselectable = "on";
33454     /** @private */
33455     this.resizingEl = Roo.get(cfg.resizingElement, true);
33456
33457     /**
33458      * @private
33459      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33460      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33461      * @type Number
33462      */
33463     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33464     
33465     /**
33466      * The minimum size of the resizing element. (Defaults to 0)
33467      * @type Number
33468      */
33469     this.minSize = 0;
33470     
33471     /**
33472      * The maximum size of the resizing element. (Defaults to 2000)
33473      * @type Number
33474      */
33475     this.maxSize = 2000;
33476     
33477     /**
33478      * Whether to animate the transition to the new size
33479      * @type Boolean
33480      */
33481     this.animate = false;
33482     
33483     /**
33484      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33485      * @type Boolean
33486      */
33487     this.useShim = false;
33488     
33489     /** @private */
33490     this.shim = null;
33491     
33492     if(!cfg.existingProxy){
33493         /** @private */
33494         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33495     }else{
33496         this.proxy = Roo.get(cfg.existingProxy).dom;
33497     }
33498     /** @private */
33499     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33500     
33501     /** @private */
33502     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33503     
33504     /** @private */
33505     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33506     
33507     /** @private */
33508     this.dragSpecs = {};
33509     
33510     /**
33511      * @private The adapter to use to positon and resize elements
33512      */
33513     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33514     this.adapter.init(this);
33515     
33516     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33517         /** @private */
33518         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33519         this.el.addClass("roo-splitbar-h");
33520     }else{
33521         /** @private */
33522         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33523         this.el.addClass("roo-splitbar-v");
33524     }
33525     
33526     this.addEvents({
33527         /**
33528          * @event resize
33529          * Fires when the splitter is moved (alias for {@link #event-moved})
33530          * @param {Roo.bootstrap.SplitBar} this
33531          * @param {Number} newSize the new width or height
33532          */
33533         "resize" : true,
33534         /**
33535          * @event moved
33536          * Fires when the splitter is moved
33537          * @param {Roo.bootstrap.SplitBar} this
33538          * @param {Number} newSize the new width or height
33539          */
33540         "moved" : true,
33541         /**
33542          * @event beforeresize
33543          * Fires before the splitter is dragged
33544          * @param {Roo.bootstrap.SplitBar} this
33545          */
33546         "beforeresize" : true,
33547
33548         "beforeapply" : true
33549     });
33550
33551     Roo.util.Observable.call(this);
33552 };
33553
33554 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33555     onStartProxyDrag : function(x, y){
33556         this.fireEvent("beforeresize", this);
33557         if(!this.overlay){
33558             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33559             o.unselectable();
33560             o.enableDisplayMode("block");
33561             // all splitbars share the same overlay
33562             Roo.bootstrap.SplitBar.prototype.overlay = o;
33563         }
33564         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33565         this.overlay.show();
33566         Roo.get(this.proxy).setDisplayed("block");
33567         var size = this.adapter.getElementSize(this);
33568         this.activeMinSize = this.getMinimumSize();;
33569         this.activeMaxSize = this.getMaximumSize();;
33570         var c1 = size - this.activeMinSize;
33571         var c2 = Math.max(this.activeMaxSize - size, 0);
33572         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33573             this.dd.resetConstraints();
33574             this.dd.setXConstraint(
33575                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33576                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33577             );
33578             this.dd.setYConstraint(0, 0);
33579         }else{
33580             this.dd.resetConstraints();
33581             this.dd.setXConstraint(0, 0);
33582             this.dd.setYConstraint(
33583                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33584                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33585             );
33586          }
33587         this.dragSpecs.startSize = size;
33588         this.dragSpecs.startPoint = [x, y];
33589         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33590     },
33591     
33592     /** 
33593      * @private Called after the drag operation by the DDProxy
33594      */
33595     onEndProxyDrag : function(e){
33596         Roo.get(this.proxy).setDisplayed(false);
33597         var endPoint = Roo.lib.Event.getXY(e);
33598         if(this.overlay){
33599             this.overlay.hide();
33600         }
33601         var newSize;
33602         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33603             newSize = this.dragSpecs.startSize + 
33604                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33605                     endPoint[0] - this.dragSpecs.startPoint[0] :
33606                     this.dragSpecs.startPoint[0] - endPoint[0]
33607                 );
33608         }else{
33609             newSize = this.dragSpecs.startSize + 
33610                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33611                     endPoint[1] - this.dragSpecs.startPoint[1] :
33612                     this.dragSpecs.startPoint[1] - endPoint[1]
33613                 );
33614         }
33615         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33616         if(newSize != this.dragSpecs.startSize){
33617             if(this.fireEvent('beforeapply', this, newSize) !== false){
33618                 this.adapter.setElementSize(this, newSize);
33619                 this.fireEvent("moved", this, newSize);
33620                 this.fireEvent("resize", this, newSize);
33621             }
33622         }
33623     },
33624     
33625     /**
33626      * Get the adapter this SplitBar uses
33627      * @return The adapter object
33628      */
33629     getAdapter : function(){
33630         return this.adapter;
33631     },
33632     
33633     /**
33634      * Set the adapter this SplitBar uses
33635      * @param {Object} adapter A SplitBar adapter object
33636      */
33637     setAdapter : function(adapter){
33638         this.adapter = adapter;
33639         this.adapter.init(this);
33640     },
33641     
33642     /**
33643      * Gets the minimum size for the resizing element
33644      * @return {Number} The minimum size
33645      */
33646     getMinimumSize : function(){
33647         return this.minSize;
33648     },
33649     
33650     /**
33651      * Sets the minimum size for the resizing element
33652      * @param {Number} minSize The minimum size
33653      */
33654     setMinimumSize : function(minSize){
33655         this.minSize = minSize;
33656     },
33657     
33658     /**
33659      * Gets the maximum size for the resizing element
33660      * @return {Number} The maximum size
33661      */
33662     getMaximumSize : function(){
33663         return this.maxSize;
33664     },
33665     
33666     /**
33667      * Sets the maximum size for the resizing element
33668      * @param {Number} maxSize The maximum size
33669      */
33670     setMaximumSize : function(maxSize){
33671         this.maxSize = maxSize;
33672     },
33673     
33674     /**
33675      * Sets the initialize size for the resizing element
33676      * @param {Number} size The initial size
33677      */
33678     setCurrentSize : function(size){
33679         var oldAnimate = this.animate;
33680         this.animate = false;
33681         this.adapter.setElementSize(this, size);
33682         this.animate = oldAnimate;
33683     },
33684     
33685     /**
33686      * Destroy this splitbar. 
33687      * @param {Boolean} removeEl True to remove the element
33688      */
33689     destroy : function(removeEl){
33690         if(this.shim){
33691             this.shim.remove();
33692         }
33693         this.dd.unreg();
33694         this.proxy.parentNode.removeChild(this.proxy);
33695         if(removeEl){
33696             this.el.remove();
33697         }
33698     }
33699 });
33700
33701 /**
33702  * @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.
33703  */
33704 Roo.bootstrap.SplitBar.createProxy = function(dir){
33705     var proxy = new Roo.Element(document.createElement("div"));
33706     proxy.unselectable();
33707     var cls = 'roo-splitbar-proxy';
33708     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33709     document.body.appendChild(proxy.dom);
33710     return proxy.dom;
33711 };
33712
33713 /** 
33714  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33715  * Default Adapter. It assumes the splitter and resizing element are not positioned
33716  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33717  */
33718 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33719 };
33720
33721 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33722     // do nothing for now
33723     init : function(s){
33724     
33725     },
33726     /**
33727      * Called before drag operations to get the current size of the resizing element. 
33728      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33729      */
33730      getElementSize : function(s){
33731         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33732             return s.resizingEl.getWidth();
33733         }else{
33734             return s.resizingEl.getHeight();
33735         }
33736     },
33737     
33738     /**
33739      * Called after drag operations to set the size of the resizing element.
33740      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33741      * @param {Number} newSize The new size to set
33742      * @param {Function} onComplete A function to be invoked when resizing is complete
33743      */
33744     setElementSize : function(s, newSize, onComplete){
33745         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33746             if(!s.animate){
33747                 s.resizingEl.setWidth(newSize);
33748                 if(onComplete){
33749                     onComplete(s, newSize);
33750                 }
33751             }else{
33752                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33753             }
33754         }else{
33755             
33756             if(!s.animate){
33757                 s.resizingEl.setHeight(newSize);
33758                 if(onComplete){
33759                     onComplete(s, newSize);
33760                 }
33761             }else{
33762                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33763             }
33764         }
33765     }
33766 };
33767
33768 /** 
33769  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33770  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33771  * Adapter that  moves the splitter element to align with the resized sizing element. 
33772  * Used with an absolute positioned SplitBar.
33773  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33774  * document.body, make sure you assign an id to the body element.
33775  */
33776 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33777     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33778     this.container = Roo.get(container);
33779 };
33780
33781 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33782     init : function(s){
33783         this.basic.init(s);
33784     },
33785     
33786     getElementSize : function(s){
33787         return this.basic.getElementSize(s);
33788     },
33789     
33790     setElementSize : function(s, newSize, onComplete){
33791         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33792     },
33793     
33794     moveSplitter : function(s){
33795         var yes = Roo.bootstrap.SplitBar;
33796         switch(s.placement){
33797             case yes.LEFT:
33798                 s.el.setX(s.resizingEl.getRight());
33799                 break;
33800             case yes.RIGHT:
33801                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33802                 break;
33803             case yes.TOP:
33804                 s.el.setY(s.resizingEl.getBottom());
33805                 break;
33806             case yes.BOTTOM:
33807                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33808                 break;
33809         }
33810     }
33811 };
33812
33813 /**
33814  * Orientation constant - Create a vertical SplitBar
33815  * @static
33816  * @type Number
33817  */
33818 Roo.bootstrap.SplitBar.VERTICAL = 1;
33819
33820 /**
33821  * Orientation constant - Create a horizontal SplitBar
33822  * @static
33823  * @type Number
33824  */
33825 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33826
33827 /**
33828  * Placement constant - The resizing element is to the left of the splitter element
33829  * @static
33830  * @type Number
33831  */
33832 Roo.bootstrap.SplitBar.LEFT = 1;
33833
33834 /**
33835  * Placement constant - The resizing element is to the right of the splitter element
33836  * @static
33837  * @type Number
33838  */
33839 Roo.bootstrap.SplitBar.RIGHT = 2;
33840
33841 /**
33842  * Placement constant - The resizing element is positioned above the splitter element
33843  * @static
33844  * @type Number
33845  */
33846 Roo.bootstrap.SplitBar.TOP = 3;
33847
33848 /**
33849  * Placement constant - The resizing element is positioned under splitter element
33850  * @static
33851  * @type Number
33852  */
33853 Roo.bootstrap.SplitBar.BOTTOM = 4;
33854 Roo.namespace("Roo.bootstrap.layout");/*
33855  * Based on:
33856  * Ext JS Library 1.1.1
33857  * Copyright(c) 2006-2007, Ext JS, LLC.
33858  *
33859  * Originally Released Under LGPL - original licence link has changed is not relivant.
33860  *
33861  * Fork - LGPL
33862  * <script type="text/javascript">
33863  */
33864
33865 /**
33866  * @class Roo.bootstrap.layout.Manager
33867  * @extends Roo.bootstrap.Component
33868  * Base class for layout managers.
33869  */
33870 Roo.bootstrap.layout.Manager = function(config)
33871 {
33872     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33873
33874
33875
33876
33877
33878     /** false to disable window resize monitoring @type Boolean */
33879     this.monitorWindowResize = true;
33880     this.regions = {};
33881     this.addEvents({
33882         /**
33883          * @event layout
33884          * Fires when a layout is performed.
33885          * @param {Roo.LayoutManager} this
33886          */
33887         "layout" : true,
33888         /**
33889          * @event regionresized
33890          * Fires when the user resizes a region.
33891          * @param {Roo.LayoutRegion} region The resized region
33892          * @param {Number} newSize The new size (width for east/west, height for north/south)
33893          */
33894         "regionresized" : true,
33895         /**
33896          * @event regioncollapsed
33897          * Fires when a region is collapsed.
33898          * @param {Roo.LayoutRegion} region The collapsed region
33899          */
33900         "regioncollapsed" : true,
33901         /**
33902          * @event regionexpanded
33903          * Fires when a region is expanded.
33904          * @param {Roo.LayoutRegion} region The expanded region
33905          */
33906         "regionexpanded" : true
33907     });
33908     this.updating = false;
33909
33910     if (config.el) {
33911         this.el = Roo.get(config.el);
33912         this.initEvents();
33913     }
33914
33915 };
33916
33917 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33918
33919
33920     regions : null,
33921
33922     monitorWindowResize : true,
33923
33924
33925     updating : false,
33926
33927
33928     onRender : function(ct, position)
33929     {
33930         if(!this.el){
33931             this.el = Roo.get(ct);
33932             this.initEvents();
33933         }
33934         //this.fireEvent('render',this);
33935     },
33936
33937
33938     initEvents: function()
33939     {
33940
33941
33942         // ie scrollbar fix
33943         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33944             document.body.scroll = "no";
33945         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33946             this.el.position('relative');
33947         }
33948         this.id = this.el.id;
33949         this.el.addClass("roo-layout-container");
33950         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33951         if(this.el.dom != document.body ) {
33952             this.el.on('resize', this.layout,this);
33953             this.el.on('show', this.layout,this);
33954         }
33955
33956     },
33957
33958     /**
33959      * Returns true if this layout is currently being updated
33960      * @return {Boolean}
33961      */
33962     isUpdating : function(){
33963         return this.updating;
33964     },
33965
33966     /**
33967      * Suspend the LayoutManager from doing auto-layouts while
33968      * making multiple add or remove calls
33969      */
33970     beginUpdate : function(){
33971         this.updating = true;
33972     },
33973
33974     /**
33975      * Restore auto-layouts and optionally disable the manager from performing a layout
33976      * @param {Boolean} noLayout true to disable a layout update
33977      */
33978     endUpdate : function(noLayout){
33979         this.updating = false;
33980         if(!noLayout){
33981             this.layout();
33982         }
33983     },
33984
33985     layout: function(){
33986         // abstract...
33987     },
33988
33989     onRegionResized : function(region, newSize){
33990         this.fireEvent("regionresized", region, newSize);
33991         this.layout();
33992     },
33993
33994     onRegionCollapsed : function(region){
33995         this.fireEvent("regioncollapsed", region);
33996     },
33997
33998     onRegionExpanded : function(region){
33999         this.fireEvent("regionexpanded", region);
34000     },
34001
34002     /**
34003      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34004      * performs box-model adjustments.
34005      * @return {Object} The size as an object {width: (the width), height: (the height)}
34006      */
34007     getViewSize : function()
34008     {
34009         var size;
34010         if(this.el.dom != document.body){
34011             size = this.el.getSize();
34012         }else{
34013             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34014         }
34015         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34016         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34017         return size;
34018     },
34019
34020     /**
34021      * Returns the Element this layout is bound to.
34022      * @return {Roo.Element}
34023      */
34024     getEl : function(){
34025         return this.el;
34026     },
34027
34028     /**
34029      * Returns the specified region.
34030      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34031      * @return {Roo.LayoutRegion}
34032      */
34033     getRegion : function(target){
34034         return this.regions[target.toLowerCase()];
34035     },
34036
34037     onWindowResize : function(){
34038         if(this.monitorWindowResize){
34039             this.layout();
34040         }
34041     }
34042 });
34043 /*
34044  * Based on:
34045  * Ext JS Library 1.1.1
34046  * Copyright(c) 2006-2007, Ext JS, LLC.
34047  *
34048  * Originally Released Under LGPL - original licence link has changed is not relivant.
34049  *
34050  * Fork - LGPL
34051  * <script type="text/javascript">
34052  */
34053 /**
34054  * @class Roo.bootstrap.layout.Border
34055  * @extends Roo.bootstrap.layout.Manager
34056  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34057  * please see: examples/bootstrap/nested.html<br><br>
34058  
34059 <b>The container the layout is rendered into can be either the body element or any other element.
34060 If it is not the body element, the container needs to either be an absolute positioned element,
34061 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34062 the container size if it is not the body element.</b>
34063
34064 * @constructor
34065 * Create a new Border
34066 * @param {Object} config Configuration options
34067  */
34068 Roo.bootstrap.layout.Border = function(config){
34069     config = config || {};
34070     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34071     
34072     
34073     
34074     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34075         if(config[region]){
34076             config[region].region = region;
34077             this.addRegion(config[region]);
34078         }
34079     },this);
34080     
34081 };
34082
34083 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34084
34085 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34086     /**
34087      * Creates and adds a new region if it doesn't already exist.
34088      * @param {String} target The target region key (north, south, east, west or center).
34089      * @param {Object} config The regions config object
34090      * @return {BorderLayoutRegion} The new region
34091      */
34092     addRegion : function(config)
34093     {
34094         if(!this.regions[config.region]){
34095             var r = this.factory(config);
34096             this.bindRegion(r);
34097         }
34098         return this.regions[config.region];
34099     },
34100
34101     // private (kinda)
34102     bindRegion : function(r){
34103         this.regions[r.config.region] = r;
34104         
34105         r.on("visibilitychange",    this.layout, this);
34106         r.on("paneladded",          this.layout, this);
34107         r.on("panelremoved",        this.layout, this);
34108         r.on("invalidated",         this.layout, this);
34109         r.on("resized",             this.onRegionResized, this);
34110         r.on("collapsed",           this.onRegionCollapsed, this);
34111         r.on("expanded",            this.onRegionExpanded, this);
34112     },
34113
34114     /**
34115      * Performs a layout update.
34116      */
34117     layout : function()
34118     {
34119         if(this.updating) {
34120             return;
34121         }
34122         
34123         // render all the rebions if they have not been done alreayd?
34124         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34125             if(this.regions[region] && !this.regions[region].bodyEl){
34126                 this.regions[region].onRender(this.el)
34127             }
34128         },this);
34129         
34130         var size = this.getViewSize();
34131         var w = size.width;
34132         var h = size.height;
34133         var centerW = w;
34134         var centerH = h;
34135         var centerY = 0;
34136         var centerX = 0;
34137         //var x = 0, y = 0;
34138
34139         var rs = this.regions;
34140         var north = rs["north"];
34141         var south = rs["south"]; 
34142         var west = rs["west"];
34143         var east = rs["east"];
34144         var center = rs["center"];
34145         //if(this.hideOnLayout){ // not supported anymore
34146             //c.el.setStyle("display", "none");
34147         //}
34148         if(north && north.isVisible()){
34149             var b = north.getBox();
34150             var m = north.getMargins();
34151             b.width = w - (m.left+m.right);
34152             b.x = m.left;
34153             b.y = m.top;
34154             centerY = b.height + b.y + m.bottom;
34155             centerH -= centerY;
34156             north.updateBox(this.safeBox(b));
34157         }
34158         if(south && south.isVisible()){
34159             var b = south.getBox();
34160             var m = south.getMargins();
34161             b.width = w - (m.left+m.right);
34162             b.x = m.left;
34163             var totalHeight = (b.height + m.top + m.bottom);
34164             b.y = h - totalHeight + m.top;
34165             centerH -= totalHeight;
34166             south.updateBox(this.safeBox(b));
34167         }
34168         if(west && west.isVisible()){
34169             var b = west.getBox();
34170             var m = west.getMargins();
34171             b.height = centerH - (m.top+m.bottom);
34172             b.x = m.left;
34173             b.y = centerY + m.top;
34174             var totalWidth = (b.width + m.left + m.right);
34175             centerX += totalWidth;
34176             centerW -= totalWidth;
34177             west.updateBox(this.safeBox(b));
34178         }
34179         if(east && east.isVisible()){
34180             var b = east.getBox();
34181             var m = east.getMargins();
34182             b.height = centerH - (m.top+m.bottom);
34183             var totalWidth = (b.width + m.left + m.right);
34184             b.x = w - totalWidth + m.left;
34185             b.y = centerY + m.top;
34186             centerW -= totalWidth;
34187             east.updateBox(this.safeBox(b));
34188         }
34189         if(center){
34190             var m = center.getMargins();
34191             var centerBox = {
34192                 x: centerX + m.left,
34193                 y: centerY + m.top,
34194                 width: centerW - (m.left+m.right),
34195                 height: centerH - (m.top+m.bottom)
34196             };
34197             //if(this.hideOnLayout){
34198                 //center.el.setStyle("display", "block");
34199             //}
34200             center.updateBox(this.safeBox(centerBox));
34201         }
34202         this.el.repaint();
34203         this.fireEvent("layout", this);
34204     },
34205
34206     // private
34207     safeBox : function(box){
34208         box.width = Math.max(0, box.width);
34209         box.height = Math.max(0, box.height);
34210         return box;
34211     },
34212
34213     /**
34214      * Adds a ContentPanel (or subclass) to this layout.
34215      * @param {String} target The target region key (north, south, east, west or center).
34216      * @param {Roo.ContentPanel} panel The panel to add
34217      * @return {Roo.ContentPanel} The added panel
34218      */
34219     add : function(target, panel){
34220          
34221         target = target.toLowerCase();
34222         return this.regions[target].add(panel);
34223     },
34224
34225     /**
34226      * Remove a ContentPanel (or subclass) to this layout.
34227      * @param {String} target The target region key (north, south, east, west or center).
34228      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34229      * @return {Roo.ContentPanel} The removed panel
34230      */
34231     remove : function(target, panel){
34232         target = target.toLowerCase();
34233         return this.regions[target].remove(panel);
34234     },
34235
34236     /**
34237      * Searches all regions for a panel with the specified id
34238      * @param {String} panelId
34239      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34240      */
34241     findPanel : function(panelId){
34242         var rs = this.regions;
34243         for(var target in rs){
34244             if(typeof rs[target] != "function"){
34245                 var p = rs[target].getPanel(panelId);
34246                 if(p){
34247                     return p;
34248                 }
34249             }
34250         }
34251         return null;
34252     },
34253
34254     /**
34255      * Searches all regions for a panel with the specified id and activates (shows) it.
34256      * @param {String/ContentPanel} panelId The panels id or the panel itself
34257      * @return {Roo.ContentPanel} The shown panel or null
34258      */
34259     showPanel : function(panelId) {
34260       var rs = this.regions;
34261       for(var target in rs){
34262          var r = rs[target];
34263          if(typeof r != "function"){
34264             if(r.hasPanel(panelId)){
34265                return r.showPanel(panelId);
34266             }
34267          }
34268       }
34269       return null;
34270    },
34271
34272    /**
34273      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34274      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34275      */
34276    /*
34277     restoreState : function(provider){
34278         if(!provider){
34279             provider = Roo.state.Manager;
34280         }
34281         var sm = new Roo.LayoutStateManager();
34282         sm.init(this, provider);
34283     },
34284 */
34285  
34286  
34287     /**
34288      * Adds a xtype elements to the layout.
34289      * <pre><code>
34290
34291 layout.addxtype({
34292        xtype : 'ContentPanel',
34293        region: 'west',
34294        items: [ .... ]
34295    }
34296 );
34297
34298 layout.addxtype({
34299         xtype : 'NestedLayoutPanel',
34300         region: 'west',
34301         layout: {
34302            center: { },
34303            west: { }   
34304         },
34305         items : [ ... list of content panels or nested layout panels.. ]
34306    }
34307 );
34308 </code></pre>
34309      * @param {Object} cfg Xtype definition of item to add.
34310      */
34311     addxtype : function(cfg)
34312     {
34313         // basically accepts a pannel...
34314         // can accept a layout region..!?!?
34315         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34316         
34317         
34318         // theory?  children can only be panels??
34319         
34320         //if (!cfg.xtype.match(/Panel$/)) {
34321         //    return false;
34322         //}
34323         var ret = false;
34324         
34325         if (typeof(cfg.region) == 'undefined') {
34326             Roo.log("Failed to add Panel, region was not set");
34327             Roo.log(cfg);
34328             return false;
34329         }
34330         var region = cfg.region;
34331         delete cfg.region;
34332         
34333           
34334         var xitems = [];
34335         if (cfg.items) {
34336             xitems = cfg.items;
34337             delete cfg.items;
34338         }
34339         var nb = false;
34340         
34341         switch(cfg.xtype) 
34342         {
34343             case 'Content':  // ContentPanel (el, cfg)
34344             case 'Scroll':  // ContentPanel (el, cfg)
34345             case 'View': 
34346                 cfg.autoCreate = true;
34347                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34348                 //} else {
34349                 //    var el = this.el.createChild();
34350                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34351                 //}
34352                 
34353                 this.add(region, ret);
34354                 break;
34355             
34356             /*
34357             case 'TreePanel': // our new panel!
34358                 cfg.el = this.el.createChild();
34359                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34360                 this.add(region, ret);
34361                 break;
34362             */
34363             
34364             case 'Nest': 
34365                 // create a new Layout (which is  a Border Layout...
34366                 
34367                 var clayout = cfg.layout;
34368                 clayout.el  = this.el.createChild();
34369                 clayout.items   = clayout.items  || [];
34370                 
34371                 delete cfg.layout;
34372                 
34373                 // replace this exitems with the clayout ones..
34374                 xitems = clayout.items;
34375                  
34376                 // force background off if it's in center...
34377                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34378                     cfg.background = false;
34379                 }
34380                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34381                 
34382                 
34383                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34384                 //console.log('adding nested layout panel '  + cfg.toSource());
34385                 this.add(region, ret);
34386                 nb = {}; /// find first...
34387                 break;
34388             
34389             case 'Grid':
34390                 
34391                 // needs grid and region
34392                 
34393                 //var el = this.getRegion(region).el.createChild();
34394                 /*
34395                  *var el = this.el.createChild();
34396                 // create the grid first...
34397                 cfg.grid.container = el;
34398                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34399                 */
34400                 
34401                 if (region == 'center' && this.active ) {
34402                     cfg.background = false;
34403                 }
34404                 
34405                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34406                 
34407                 this.add(region, ret);
34408                 /*
34409                 if (cfg.background) {
34410                     // render grid on panel activation (if panel background)
34411                     ret.on('activate', function(gp) {
34412                         if (!gp.grid.rendered) {
34413                     //        gp.grid.render(el);
34414                         }
34415                     });
34416                 } else {
34417                   //  cfg.grid.render(el);
34418                 }
34419                 */
34420                 break;
34421            
34422            
34423             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34424                 // it was the old xcomponent building that caused this before.
34425                 // espeically if border is the top element in the tree.
34426                 ret = this;
34427                 break; 
34428                 
34429                     
34430                 
34431                 
34432                 
34433             default:
34434                 /*
34435                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34436                     
34437                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34438                     this.add(region, ret);
34439                 } else {
34440                 */
34441                     Roo.log(cfg);
34442                     throw "Can not add '" + cfg.xtype + "' to Border";
34443                     return null;
34444              
34445                                 
34446              
34447         }
34448         this.beginUpdate();
34449         // add children..
34450         var region = '';
34451         var abn = {};
34452         Roo.each(xitems, function(i)  {
34453             region = nb && i.region ? i.region : false;
34454             
34455             var add = ret.addxtype(i);
34456            
34457             if (region) {
34458                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34459                 if (!i.background) {
34460                     abn[region] = nb[region] ;
34461                 }
34462             }
34463             
34464         });
34465         this.endUpdate();
34466
34467         // make the last non-background panel active..
34468         //if (nb) { Roo.log(abn); }
34469         if (nb) {
34470             
34471             for(var r in abn) {
34472                 region = this.getRegion(r);
34473                 if (region) {
34474                     // tried using nb[r], but it does not work..
34475                      
34476                     region.showPanel(abn[r]);
34477                    
34478                 }
34479             }
34480         }
34481         return ret;
34482         
34483     },
34484     
34485     
34486 // private
34487     factory : function(cfg)
34488     {
34489         
34490         var validRegions = Roo.bootstrap.layout.Border.regions;
34491
34492         var target = cfg.region;
34493         cfg.mgr = this;
34494         
34495         var r = Roo.bootstrap.layout;
34496         Roo.log(target);
34497         switch(target){
34498             case "north":
34499                 return new r.North(cfg);
34500             case "south":
34501                 return new r.South(cfg);
34502             case "east":
34503                 return new r.East(cfg);
34504             case "west":
34505                 return new r.West(cfg);
34506             case "center":
34507                 return new r.Center(cfg);
34508         }
34509         throw 'Layout region "'+target+'" not supported.';
34510     }
34511     
34512     
34513 });
34514  /*
34515  * Based on:
34516  * Ext JS Library 1.1.1
34517  * Copyright(c) 2006-2007, Ext JS, LLC.
34518  *
34519  * Originally Released Under LGPL - original licence link has changed is not relivant.
34520  *
34521  * Fork - LGPL
34522  * <script type="text/javascript">
34523  */
34524  
34525 /**
34526  * @class Roo.bootstrap.layout.Basic
34527  * @extends Roo.util.Observable
34528  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34529  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34530  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34531  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34532  * @cfg {string}   region  the region that it inhabits..
34533  * @cfg {bool}   skipConfig skip config?
34534  * 
34535
34536  */
34537 Roo.bootstrap.layout.Basic = function(config){
34538     
34539     this.mgr = config.mgr;
34540     
34541     this.position = config.region;
34542     
34543     var skipConfig = config.skipConfig;
34544     
34545     this.events = {
34546         /**
34547          * @scope Roo.BasicLayoutRegion
34548          */
34549         
34550         /**
34551          * @event beforeremove
34552          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34553          * @param {Roo.LayoutRegion} this
34554          * @param {Roo.ContentPanel} panel The panel
34555          * @param {Object} e The cancel event object
34556          */
34557         "beforeremove" : true,
34558         /**
34559          * @event invalidated
34560          * Fires when the layout for this region is changed.
34561          * @param {Roo.LayoutRegion} this
34562          */
34563         "invalidated" : true,
34564         /**
34565          * @event visibilitychange
34566          * Fires when this region is shown or hidden 
34567          * @param {Roo.LayoutRegion} this
34568          * @param {Boolean} visibility true or false
34569          */
34570         "visibilitychange" : true,
34571         /**
34572          * @event paneladded
34573          * Fires when a panel is added. 
34574          * @param {Roo.LayoutRegion} this
34575          * @param {Roo.ContentPanel} panel The panel
34576          */
34577         "paneladded" : true,
34578         /**
34579          * @event panelremoved
34580          * Fires when a panel is removed. 
34581          * @param {Roo.LayoutRegion} this
34582          * @param {Roo.ContentPanel} panel The panel
34583          */
34584         "panelremoved" : true,
34585         /**
34586          * @event beforecollapse
34587          * Fires when this region before collapse.
34588          * @param {Roo.LayoutRegion} this
34589          */
34590         "beforecollapse" : true,
34591         /**
34592          * @event collapsed
34593          * Fires when this region is collapsed.
34594          * @param {Roo.LayoutRegion} this
34595          */
34596         "collapsed" : true,
34597         /**
34598          * @event expanded
34599          * Fires when this region is expanded.
34600          * @param {Roo.LayoutRegion} this
34601          */
34602         "expanded" : true,
34603         /**
34604          * @event slideshow
34605          * Fires when this region is slid into view.
34606          * @param {Roo.LayoutRegion} this
34607          */
34608         "slideshow" : true,
34609         /**
34610          * @event slidehide
34611          * Fires when this region slides out of view. 
34612          * @param {Roo.LayoutRegion} this
34613          */
34614         "slidehide" : true,
34615         /**
34616          * @event panelactivated
34617          * Fires when a panel is activated. 
34618          * @param {Roo.LayoutRegion} this
34619          * @param {Roo.ContentPanel} panel The activated panel
34620          */
34621         "panelactivated" : true,
34622         /**
34623          * @event resized
34624          * Fires when the user resizes this region. 
34625          * @param {Roo.LayoutRegion} this
34626          * @param {Number} newSize The new size (width for east/west, height for north/south)
34627          */
34628         "resized" : true
34629     };
34630     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34631     this.panels = new Roo.util.MixedCollection();
34632     this.panels.getKey = this.getPanelId.createDelegate(this);
34633     this.box = null;
34634     this.activePanel = null;
34635     // ensure listeners are added...
34636     
34637     if (config.listeners || config.events) {
34638         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34639             listeners : config.listeners || {},
34640             events : config.events || {}
34641         });
34642     }
34643     
34644     if(skipConfig !== true){
34645         this.applyConfig(config);
34646     }
34647 };
34648
34649 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34650 {
34651     getPanelId : function(p){
34652         return p.getId();
34653     },
34654     
34655     applyConfig : function(config){
34656         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34657         this.config = config;
34658         
34659     },
34660     
34661     /**
34662      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34663      * the width, for horizontal (north, south) the height.
34664      * @param {Number} newSize The new width or height
34665      */
34666     resizeTo : function(newSize){
34667         var el = this.el ? this.el :
34668                  (this.activePanel ? this.activePanel.getEl() : null);
34669         if(el){
34670             switch(this.position){
34671                 case "east":
34672                 case "west":
34673                     el.setWidth(newSize);
34674                     this.fireEvent("resized", this, newSize);
34675                 break;
34676                 case "north":
34677                 case "south":
34678                     el.setHeight(newSize);
34679                     this.fireEvent("resized", this, newSize);
34680                 break;                
34681             }
34682         }
34683     },
34684     
34685     getBox : function(){
34686         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34687     },
34688     
34689     getMargins : function(){
34690         return this.margins;
34691     },
34692     
34693     updateBox : function(box){
34694         this.box = box;
34695         var el = this.activePanel.getEl();
34696         el.dom.style.left = box.x + "px";
34697         el.dom.style.top = box.y + "px";
34698         this.activePanel.setSize(box.width, box.height);
34699     },
34700     
34701     /**
34702      * Returns the container element for this region.
34703      * @return {Roo.Element}
34704      */
34705     getEl : function(){
34706         return this.activePanel;
34707     },
34708     
34709     /**
34710      * Returns true if this region is currently visible.
34711      * @return {Boolean}
34712      */
34713     isVisible : function(){
34714         return this.activePanel ? true : false;
34715     },
34716     
34717     setActivePanel : function(panel){
34718         panel = this.getPanel(panel);
34719         if(this.activePanel && this.activePanel != panel){
34720             this.activePanel.setActiveState(false);
34721             this.activePanel.getEl().setLeftTop(-10000,-10000);
34722         }
34723         this.activePanel = panel;
34724         panel.setActiveState(true);
34725         if(this.box){
34726             panel.setSize(this.box.width, this.box.height);
34727         }
34728         this.fireEvent("panelactivated", this, panel);
34729         this.fireEvent("invalidated");
34730     },
34731     
34732     /**
34733      * Show the specified panel.
34734      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34735      * @return {Roo.ContentPanel} The shown panel or null
34736      */
34737     showPanel : function(panel){
34738         panel = this.getPanel(panel);
34739         if(panel){
34740             this.setActivePanel(panel);
34741         }
34742         return panel;
34743     },
34744     
34745     /**
34746      * Get the active panel for this region.
34747      * @return {Roo.ContentPanel} The active panel or null
34748      */
34749     getActivePanel : function(){
34750         return this.activePanel;
34751     },
34752     
34753     /**
34754      * Add the passed ContentPanel(s)
34755      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34756      * @return {Roo.ContentPanel} The panel added (if only one was added)
34757      */
34758     add : function(panel){
34759         if(arguments.length > 1){
34760             for(var i = 0, len = arguments.length; i < len; i++) {
34761                 this.add(arguments[i]);
34762             }
34763             return null;
34764         }
34765         if(this.hasPanel(panel)){
34766             this.showPanel(panel);
34767             return panel;
34768         }
34769         var el = panel.getEl();
34770         if(el.dom.parentNode != this.mgr.el.dom){
34771             this.mgr.el.dom.appendChild(el.dom);
34772         }
34773         if(panel.setRegion){
34774             panel.setRegion(this);
34775         }
34776         this.panels.add(panel);
34777         el.setStyle("position", "absolute");
34778         if(!panel.background){
34779             this.setActivePanel(panel);
34780             if(this.config.initialSize && this.panels.getCount()==1){
34781                 this.resizeTo(this.config.initialSize);
34782             }
34783         }
34784         this.fireEvent("paneladded", this, panel);
34785         return panel;
34786     },
34787     
34788     /**
34789      * Returns true if the panel is in this region.
34790      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34791      * @return {Boolean}
34792      */
34793     hasPanel : function(panel){
34794         if(typeof panel == "object"){ // must be panel obj
34795             panel = panel.getId();
34796         }
34797         return this.getPanel(panel) ? true : false;
34798     },
34799     
34800     /**
34801      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34802      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34803      * @param {Boolean} preservePanel Overrides the config preservePanel option
34804      * @return {Roo.ContentPanel} The panel that was removed
34805      */
34806     remove : function(panel, preservePanel){
34807         panel = this.getPanel(panel);
34808         if(!panel){
34809             return null;
34810         }
34811         var e = {};
34812         this.fireEvent("beforeremove", this, panel, e);
34813         if(e.cancel === true){
34814             return null;
34815         }
34816         var panelId = panel.getId();
34817         this.panels.removeKey(panelId);
34818         return panel;
34819     },
34820     
34821     /**
34822      * Returns the panel specified or null if it's not in this region.
34823      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34824      * @return {Roo.ContentPanel}
34825      */
34826     getPanel : function(id){
34827         if(typeof id == "object"){ // must be panel obj
34828             return id;
34829         }
34830         return this.panels.get(id);
34831     },
34832     
34833     /**
34834      * Returns this regions position (north/south/east/west/center).
34835      * @return {String} 
34836      */
34837     getPosition: function(){
34838         return this.position;    
34839     }
34840 });/*
34841  * Based on:
34842  * Ext JS Library 1.1.1
34843  * Copyright(c) 2006-2007, Ext JS, LLC.
34844  *
34845  * Originally Released Under LGPL - original licence link has changed is not relivant.
34846  *
34847  * Fork - LGPL
34848  * <script type="text/javascript">
34849  */
34850  
34851 /**
34852  * @class Roo.bootstrap.layout.Region
34853  * @extends Roo.bootstrap.layout.Basic
34854  * This class represents a region in a layout manager.
34855  
34856  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34857  * @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})
34858  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34859  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34860  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34861  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34862  * @cfg {String}    title           The title for the region (overrides panel titles)
34863  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34864  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34865  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34866  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34867  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34868  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34869  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34870  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34871  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34872  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34873
34874  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34875  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34876  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34877  * @cfg {Number}    width           For East/West panels
34878  * @cfg {Number}    height          For North/South panels
34879  * @cfg {Boolean}   split           To show the splitter
34880  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34881  * 
34882  * @cfg {string}   cls             Extra CSS classes to add to region
34883  * 
34884  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34885  * @cfg {string}   region  the region that it inhabits..
34886  *
34887
34888  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34889  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34890
34891  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34892  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34893  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34894  */
34895 Roo.bootstrap.layout.Region = function(config)
34896 {
34897     this.applyConfig(config);
34898
34899     var mgr = config.mgr;
34900     var pos = config.region;
34901     config.skipConfig = true;
34902     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34903     
34904     if (mgr.el) {
34905         this.onRender(mgr.el);   
34906     }
34907      
34908     this.visible = true;
34909     this.collapsed = false;
34910     this.unrendered_panels = [];
34911 };
34912
34913 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34914
34915     position: '', // set by wrapper (eg. north/south etc..)
34916     unrendered_panels : null,  // unrendered panels.
34917     createBody : function(){
34918         /** This region's body element 
34919         * @type Roo.Element */
34920         this.bodyEl = this.el.createChild({
34921                 tag: "div",
34922                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34923         });
34924     },
34925
34926     onRender: function(ctr, pos)
34927     {
34928         var dh = Roo.DomHelper;
34929         /** This region's container element 
34930         * @type Roo.Element */
34931         this.el = dh.append(ctr.dom, {
34932                 tag: "div",
34933                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34934             }, true);
34935         /** This region's title element 
34936         * @type Roo.Element */
34937     
34938         this.titleEl = dh.append(this.el.dom,
34939             {
34940                     tag: "div",
34941                     unselectable: "on",
34942                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34943                     children:[
34944                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34945                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34946                     ]}, true);
34947         
34948         this.titleEl.enableDisplayMode();
34949         /** This region's title text element 
34950         * @type HTMLElement */
34951         this.titleTextEl = this.titleEl.dom.firstChild;
34952         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34953         /*
34954         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34955         this.closeBtn.enableDisplayMode();
34956         this.closeBtn.on("click", this.closeClicked, this);
34957         this.closeBtn.hide();
34958     */
34959         this.createBody(this.config);
34960         if(this.config.hideWhenEmpty){
34961             this.hide();
34962             this.on("paneladded", this.validateVisibility, this);
34963             this.on("panelremoved", this.validateVisibility, this);
34964         }
34965         if(this.autoScroll){
34966             this.bodyEl.setStyle("overflow", "auto");
34967         }else{
34968             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34969         }
34970         //if(c.titlebar !== false){
34971             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34972                 this.titleEl.hide();
34973             }else{
34974                 this.titleEl.show();
34975                 if(this.config.title){
34976                     this.titleTextEl.innerHTML = this.config.title;
34977                 }
34978             }
34979         //}
34980         if(this.config.collapsed){
34981             this.collapse(true);
34982         }
34983         if(this.config.hidden){
34984             this.hide();
34985         }
34986         
34987         if (this.unrendered_panels && this.unrendered_panels.length) {
34988             for (var i =0;i< this.unrendered_panels.length; i++) {
34989                 this.add(this.unrendered_panels[i]);
34990             }
34991             this.unrendered_panels = null;
34992             
34993         }
34994         
34995     },
34996     
34997     applyConfig : function(c)
34998     {
34999         /*
35000          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35001             var dh = Roo.DomHelper;
35002             if(c.titlebar !== false){
35003                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35004                 this.collapseBtn.on("click", this.collapse, this);
35005                 this.collapseBtn.enableDisplayMode();
35006                 /*
35007                 if(c.showPin === true || this.showPin){
35008                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35009                     this.stickBtn.enableDisplayMode();
35010                     this.stickBtn.on("click", this.expand, this);
35011                     this.stickBtn.hide();
35012                 }
35013                 
35014             }
35015             */
35016             /** This region's collapsed element
35017             * @type Roo.Element */
35018             /*
35019              *
35020             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35021                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35022             ]}, true);
35023             
35024             if(c.floatable !== false){
35025                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35026                this.collapsedEl.on("click", this.collapseClick, this);
35027             }
35028
35029             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35030                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35031                    id: "message", unselectable: "on", style:{"float":"left"}});
35032                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35033              }
35034             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35035             this.expandBtn.on("click", this.expand, this);
35036             
35037         }
35038         
35039         if(this.collapseBtn){
35040             this.collapseBtn.setVisible(c.collapsible == true);
35041         }
35042         
35043         this.cmargins = c.cmargins || this.cmargins ||
35044                          (this.position == "west" || this.position == "east" ?
35045                              {top: 0, left: 2, right:2, bottom: 0} :
35046                              {top: 2, left: 0, right:0, bottom: 2});
35047         */
35048         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35049         
35050         
35051         this.bottomTabs = c.tabPosition != "top";
35052         
35053         this.autoScroll = c.autoScroll || false;
35054         
35055         
35056        
35057         
35058         this.duration = c.duration || .30;
35059         this.slideDuration = c.slideDuration || .45;
35060         this.config = c;
35061        
35062     },
35063     /**
35064      * Returns true if this region is currently visible.
35065      * @return {Boolean}
35066      */
35067     isVisible : function(){
35068         return this.visible;
35069     },
35070
35071     /**
35072      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35073      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35074      */
35075     //setCollapsedTitle : function(title){
35076     //    title = title || "&#160;";
35077      //   if(this.collapsedTitleTextEl){
35078       //      this.collapsedTitleTextEl.innerHTML = title;
35079        // }
35080     //},
35081
35082     getBox : function(){
35083         var b;
35084       //  if(!this.collapsed){
35085             b = this.el.getBox(false, true);
35086        // }else{
35087           //  b = this.collapsedEl.getBox(false, true);
35088         //}
35089         return b;
35090     },
35091
35092     getMargins : function(){
35093         return this.margins;
35094         //return this.collapsed ? this.cmargins : this.margins;
35095     },
35096 /*
35097     highlight : function(){
35098         this.el.addClass("x-layout-panel-dragover");
35099     },
35100
35101     unhighlight : function(){
35102         this.el.removeClass("x-layout-panel-dragover");
35103     },
35104 */
35105     updateBox : function(box)
35106     {
35107         if (!this.bodyEl) {
35108             return; // not rendered yet..
35109         }
35110         
35111         this.box = box;
35112         if(!this.collapsed){
35113             this.el.dom.style.left = box.x + "px";
35114             this.el.dom.style.top = box.y + "px";
35115             this.updateBody(box.width, box.height);
35116         }else{
35117             this.collapsedEl.dom.style.left = box.x + "px";
35118             this.collapsedEl.dom.style.top = box.y + "px";
35119             this.collapsedEl.setSize(box.width, box.height);
35120         }
35121         if(this.tabs){
35122             this.tabs.autoSizeTabs();
35123         }
35124     },
35125
35126     updateBody : function(w, h)
35127     {
35128         if(w !== null){
35129             this.el.setWidth(w);
35130             w -= this.el.getBorderWidth("rl");
35131             if(this.config.adjustments){
35132                 w += this.config.adjustments[0];
35133             }
35134         }
35135         if(h !== null && h > 0){
35136             this.el.setHeight(h);
35137             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35138             h -= this.el.getBorderWidth("tb");
35139             if(this.config.adjustments){
35140                 h += this.config.adjustments[1];
35141             }
35142             this.bodyEl.setHeight(h);
35143             if(this.tabs){
35144                 h = this.tabs.syncHeight(h);
35145             }
35146         }
35147         if(this.panelSize){
35148             w = w !== null ? w : this.panelSize.width;
35149             h = h !== null ? h : this.panelSize.height;
35150         }
35151         if(this.activePanel){
35152             var el = this.activePanel.getEl();
35153             w = w !== null ? w : el.getWidth();
35154             h = h !== null ? h : el.getHeight();
35155             this.panelSize = {width: w, height: h};
35156             this.activePanel.setSize(w, h);
35157         }
35158         if(Roo.isIE && this.tabs){
35159             this.tabs.el.repaint();
35160         }
35161     },
35162
35163     /**
35164      * Returns the container element for this region.
35165      * @return {Roo.Element}
35166      */
35167     getEl : function(){
35168         return this.el;
35169     },
35170
35171     /**
35172      * Hides this region.
35173      */
35174     hide : function(){
35175         //if(!this.collapsed){
35176             this.el.dom.style.left = "-2000px";
35177             this.el.hide();
35178         //}else{
35179          //   this.collapsedEl.dom.style.left = "-2000px";
35180          //   this.collapsedEl.hide();
35181        // }
35182         this.visible = false;
35183         this.fireEvent("visibilitychange", this, false);
35184     },
35185
35186     /**
35187      * Shows this region if it was previously hidden.
35188      */
35189     show : function(){
35190         //if(!this.collapsed){
35191             this.el.show();
35192         //}else{
35193         //    this.collapsedEl.show();
35194        // }
35195         this.visible = true;
35196         this.fireEvent("visibilitychange", this, true);
35197     },
35198 /*
35199     closeClicked : function(){
35200         if(this.activePanel){
35201             this.remove(this.activePanel);
35202         }
35203     },
35204
35205     collapseClick : function(e){
35206         if(this.isSlid){
35207            e.stopPropagation();
35208            this.slideIn();
35209         }else{
35210            e.stopPropagation();
35211            this.slideOut();
35212         }
35213     },
35214 */
35215     /**
35216      * Collapses this region.
35217      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35218      */
35219     /*
35220     collapse : function(skipAnim, skipCheck = false){
35221         if(this.collapsed) {
35222             return;
35223         }
35224         
35225         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35226             
35227             this.collapsed = true;
35228             if(this.split){
35229                 this.split.el.hide();
35230             }
35231             if(this.config.animate && skipAnim !== true){
35232                 this.fireEvent("invalidated", this);
35233                 this.animateCollapse();
35234             }else{
35235                 this.el.setLocation(-20000,-20000);
35236                 this.el.hide();
35237                 this.collapsedEl.show();
35238                 this.fireEvent("collapsed", this);
35239                 this.fireEvent("invalidated", this);
35240             }
35241         }
35242         
35243     },
35244 */
35245     animateCollapse : function(){
35246         // overridden
35247     },
35248
35249     /**
35250      * Expands this region if it was previously collapsed.
35251      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35252      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35253      */
35254     /*
35255     expand : function(e, skipAnim){
35256         if(e) {
35257             e.stopPropagation();
35258         }
35259         if(!this.collapsed || this.el.hasActiveFx()) {
35260             return;
35261         }
35262         if(this.isSlid){
35263             this.afterSlideIn();
35264             skipAnim = true;
35265         }
35266         this.collapsed = false;
35267         if(this.config.animate && skipAnim !== true){
35268             this.animateExpand();
35269         }else{
35270             this.el.show();
35271             if(this.split){
35272                 this.split.el.show();
35273             }
35274             this.collapsedEl.setLocation(-2000,-2000);
35275             this.collapsedEl.hide();
35276             this.fireEvent("invalidated", this);
35277             this.fireEvent("expanded", this);
35278         }
35279     },
35280 */
35281     animateExpand : function(){
35282         // overridden
35283     },
35284
35285     initTabs : function()
35286     {
35287         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35288         
35289         var ts = new Roo.bootstrap.panel.Tabs({
35290                 el: this.bodyEl.dom,
35291                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35292                 disableTooltips: this.config.disableTabTips,
35293                 toolbar : this.config.toolbar
35294             });
35295         
35296         if(this.config.hideTabs){
35297             ts.stripWrap.setDisplayed(false);
35298         }
35299         this.tabs = ts;
35300         ts.resizeTabs = this.config.resizeTabs === true;
35301         ts.minTabWidth = this.config.minTabWidth || 40;
35302         ts.maxTabWidth = this.config.maxTabWidth || 250;
35303         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35304         ts.monitorResize = false;
35305         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35306         ts.bodyEl.addClass('roo-layout-tabs-body');
35307         this.panels.each(this.initPanelAsTab, this);
35308     },
35309
35310     initPanelAsTab : function(panel){
35311         var ti = this.tabs.addTab(
35312             panel.getEl().id,
35313             panel.getTitle(),
35314             null,
35315             this.config.closeOnTab && panel.isClosable(),
35316             panel.tpl
35317         );
35318         if(panel.tabTip !== undefined){
35319             ti.setTooltip(panel.tabTip);
35320         }
35321         ti.on("activate", function(){
35322               this.setActivePanel(panel);
35323         }, this);
35324         
35325         if(this.config.closeOnTab){
35326             ti.on("beforeclose", function(t, e){
35327                 e.cancel = true;
35328                 this.remove(panel);
35329             }, this);
35330         }
35331         
35332         panel.tabItem = ti;
35333         
35334         return ti;
35335     },
35336
35337     updatePanelTitle : function(panel, title)
35338     {
35339         if(this.activePanel == panel){
35340             this.updateTitle(title);
35341         }
35342         if(this.tabs){
35343             var ti = this.tabs.getTab(panel.getEl().id);
35344             ti.setText(title);
35345             if(panel.tabTip !== undefined){
35346                 ti.setTooltip(panel.tabTip);
35347             }
35348         }
35349     },
35350
35351     updateTitle : function(title){
35352         if(this.titleTextEl && !this.config.title){
35353             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35354         }
35355     },
35356
35357     setActivePanel : function(panel)
35358     {
35359         panel = this.getPanel(panel);
35360         if(this.activePanel && this.activePanel != panel){
35361             this.activePanel.setActiveState(false);
35362         }
35363         this.activePanel = panel;
35364         panel.setActiveState(true);
35365         if(this.panelSize){
35366             panel.setSize(this.panelSize.width, this.panelSize.height);
35367         }
35368         if(this.closeBtn){
35369             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35370         }
35371         this.updateTitle(panel.getTitle());
35372         if(this.tabs){
35373             this.fireEvent("invalidated", this);
35374         }
35375         this.fireEvent("panelactivated", this, panel);
35376     },
35377
35378     /**
35379      * Shows the specified panel.
35380      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35381      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35382      */
35383     showPanel : function(panel)
35384     {
35385         panel = this.getPanel(panel);
35386         if(panel){
35387             if(this.tabs){
35388                 var tab = this.tabs.getTab(panel.getEl().id);
35389                 if(tab.isHidden()){
35390                     this.tabs.unhideTab(tab.id);
35391                 }
35392                 tab.activate();
35393             }else{
35394                 this.setActivePanel(panel);
35395             }
35396         }
35397         return panel;
35398     },
35399
35400     /**
35401      * Get the active panel for this region.
35402      * @return {Roo.ContentPanel} The active panel or null
35403      */
35404     getActivePanel : function(){
35405         return this.activePanel;
35406     },
35407
35408     validateVisibility : function(){
35409         if(this.panels.getCount() < 1){
35410             this.updateTitle("&#160;");
35411             this.closeBtn.hide();
35412             this.hide();
35413         }else{
35414             if(!this.isVisible()){
35415                 this.show();
35416             }
35417         }
35418     },
35419
35420     /**
35421      * Adds the passed ContentPanel(s) to this region.
35422      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35423      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35424      */
35425     add : function(panel)
35426     {
35427         if(arguments.length > 1){
35428             for(var i = 0, len = arguments.length; i < len; i++) {
35429                 this.add(arguments[i]);
35430             }
35431             return null;
35432         }
35433         
35434         // if we have not been rendered yet, then we can not really do much of this..
35435         if (!this.bodyEl) {
35436             this.unrendered_panels.push(panel);
35437             return panel;
35438         }
35439         
35440         
35441         
35442         
35443         if(this.hasPanel(panel)){
35444             this.showPanel(panel);
35445             return panel;
35446         }
35447         panel.setRegion(this);
35448         this.panels.add(panel);
35449        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35450             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35451             // and hide them... ???
35452             this.bodyEl.dom.appendChild(panel.getEl().dom);
35453             if(panel.background !== true){
35454                 this.setActivePanel(panel);
35455             }
35456             this.fireEvent("paneladded", this, panel);
35457             return panel;
35458         }
35459         */
35460         if(!this.tabs){
35461             this.initTabs();
35462         }else{
35463             this.initPanelAsTab(panel);
35464         }
35465         
35466         
35467         if(panel.background !== true){
35468             this.tabs.activate(panel.getEl().id);
35469         }
35470         this.fireEvent("paneladded", this, panel);
35471         return panel;
35472     },
35473
35474     /**
35475      * Hides the tab for the specified panel.
35476      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35477      */
35478     hidePanel : function(panel){
35479         if(this.tabs && (panel = this.getPanel(panel))){
35480             this.tabs.hideTab(panel.getEl().id);
35481         }
35482     },
35483
35484     /**
35485      * Unhides the tab for a previously hidden panel.
35486      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35487      */
35488     unhidePanel : function(panel){
35489         if(this.tabs && (panel = this.getPanel(panel))){
35490             this.tabs.unhideTab(panel.getEl().id);
35491         }
35492     },
35493
35494     clearPanels : function(){
35495         while(this.panels.getCount() > 0){
35496              this.remove(this.panels.first());
35497         }
35498     },
35499
35500     /**
35501      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35502      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35503      * @param {Boolean} preservePanel Overrides the config preservePanel option
35504      * @return {Roo.ContentPanel} The panel that was removed
35505      */
35506     remove : function(panel, preservePanel)
35507     {
35508         panel = this.getPanel(panel);
35509         if(!panel){
35510             return null;
35511         }
35512         var e = {};
35513         this.fireEvent("beforeremove", this, panel, e);
35514         if(e.cancel === true){
35515             return null;
35516         }
35517         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35518         var panelId = panel.getId();
35519         this.panels.removeKey(panelId);
35520         if(preservePanel){
35521             document.body.appendChild(panel.getEl().dom);
35522         }
35523         if(this.tabs){
35524             this.tabs.removeTab(panel.getEl().id);
35525         }else if (!preservePanel){
35526             this.bodyEl.dom.removeChild(panel.getEl().dom);
35527         }
35528         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35529             var p = this.panels.first();
35530             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35531             tempEl.appendChild(p.getEl().dom);
35532             this.bodyEl.update("");
35533             this.bodyEl.dom.appendChild(p.getEl().dom);
35534             tempEl = null;
35535             this.updateTitle(p.getTitle());
35536             this.tabs = null;
35537             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35538             this.setActivePanel(p);
35539         }
35540         panel.setRegion(null);
35541         if(this.activePanel == panel){
35542             this.activePanel = null;
35543         }
35544         if(this.config.autoDestroy !== false && preservePanel !== true){
35545             try{panel.destroy();}catch(e){}
35546         }
35547         this.fireEvent("panelremoved", this, panel);
35548         return panel;
35549     },
35550
35551     /**
35552      * Returns the TabPanel component used by this region
35553      * @return {Roo.TabPanel}
35554      */
35555     getTabs : function(){
35556         return this.tabs;
35557     },
35558
35559     createTool : function(parentEl, className){
35560         var btn = Roo.DomHelper.append(parentEl, {
35561             tag: "div",
35562             cls: "x-layout-tools-button",
35563             children: [ {
35564                 tag: "div",
35565                 cls: "roo-layout-tools-button-inner " + className,
35566                 html: "&#160;"
35567             }]
35568         }, true);
35569         btn.addClassOnOver("roo-layout-tools-button-over");
35570         return btn;
35571     }
35572 });/*
35573  * Based on:
35574  * Ext JS Library 1.1.1
35575  * Copyright(c) 2006-2007, Ext JS, LLC.
35576  *
35577  * Originally Released Under LGPL - original licence link has changed is not relivant.
35578  *
35579  * Fork - LGPL
35580  * <script type="text/javascript">
35581  */
35582  
35583
35584
35585 /**
35586  * @class Roo.SplitLayoutRegion
35587  * @extends Roo.LayoutRegion
35588  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35589  */
35590 Roo.bootstrap.layout.Split = function(config){
35591     this.cursor = config.cursor;
35592     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35593 };
35594
35595 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35596 {
35597     splitTip : "Drag to resize.",
35598     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35599     useSplitTips : false,
35600
35601     applyConfig : function(config){
35602         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35603     },
35604     
35605     onRender : function(ctr,pos) {
35606         
35607         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35608         if(!this.config.split){
35609             return;
35610         }
35611         if(!this.split){
35612             
35613             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35614                             tag: "div",
35615                             id: this.el.id + "-split",
35616                             cls: "roo-layout-split roo-layout-split-"+this.position,
35617                             html: "&#160;"
35618             });
35619             /** The SplitBar for this region 
35620             * @type Roo.SplitBar */
35621             // does not exist yet...
35622             Roo.log([this.position, this.orientation]);
35623             
35624             this.split = new Roo.bootstrap.SplitBar({
35625                 dragElement : splitEl,
35626                 resizingElement: this.el,
35627                 orientation : this.orientation
35628             });
35629             
35630             this.split.on("moved", this.onSplitMove, this);
35631             this.split.useShim = this.config.useShim === true;
35632             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35633             if(this.useSplitTips){
35634                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35635             }
35636             //if(config.collapsible){
35637             //    this.split.el.on("dblclick", this.collapse,  this);
35638             //}
35639         }
35640         if(typeof this.config.minSize != "undefined"){
35641             this.split.minSize = this.config.minSize;
35642         }
35643         if(typeof this.config.maxSize != "undefined"){
35644             this.split.maxSize = this.config.maxSize;
35645         }
35646         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35647             this.hideSplitter();
35648         }
35649         
35650     },
35651
35652     getHMaxSize : function(){
35653          var cmax = this.config.maxSize || 10000;
35654          var center = this.mgr.getRegion("center");
35655          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35656     },
35657
35658     getVMaxSize : function(){
35659          var cmax = this.config.maxSize || 10000;
35660          var center = this.mgr.getRegion("center");
35661          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35662     },
35663
35664     onSplitMove : function(split, newSize){
35665         this.fireEvent("resized", this, newSize);
35666     },
35667     
35668     /** 
35669      * Returns the {@link Roo.SplitBar} for this region.
35670      * @return {Roo.SplitBar}
35671      */
35672     getSplitBar : function(){
35673         return this.split;
35674     },
35675     
35676     hide : function(){
35677         this.hideSplitter();
35678         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35679     },
35680
35681     hideSplitter : function(){
35682         if(this.split){
35683             this.split.el.setLocation(-2000,-2000);
35684             this.split.el.hide();
35685         }
35686     },
35687
35688     show : function(){
35689         if(this.split){
35690             this.split.el.show();
35691         }
35692         Roo.bootstrap.layout.Split.superclass.show.call(this);
35693     },
35694     
35695     beforeSlide: function(){
35696         if(Roo.isGecko){// firefox overflow auto bug workaround
35697             this.bodyEl.clip();
35698             if(this.tabs) {
35699                 this.tabs.bodyEl.clip();
35700             }
35701             if(this.activePanel){
35702                 this.activePanel.getEl().clip();
35703                 
35704                 if(this.activePanel.beforeSlide){
35705                     this.activePanel.beforeSlide();
35706                 }
35707             }
35708         }
35709     },
35710     
35711     afterSlide : function(){
35712         if(Roo.isGecko){// firefox overflow auto bug workaround
35713             this.bodyEl.unclip();
35714             if(this.tabs) {
35715                 this.tabs.bodyEl.unclip();
35716             }
35717             if(this.activePanel){
35718                 this.activePanel.getEl().unclip();
35719                 if(this.activePanel.afterSlide){
35720                     this.activePanel.afterSlide();
35721                 }
35722             }
35723         }
35724     },
35725
35726     initAutoHide : function(){
35727         if(this.autoHide !== false){
35728             if(!this.autoHideHd){
35729                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35730                 this.autoHideHd = {
35731                     "mouseout": function(e){
35732                         if(!e.within(this.el, true)){
35733                             st.delay(500);
35734                         }
35735                     },
35736                     "mouseover" : function(e){
35737                         st.cancel();
35738                     },
35739                     scope : this
35740                 };
35741             }
35742             this.el.on(this.autoHideHd);
35743         }
35744     },
35745
35746     clearAutoHide : function(){
35747         if(this.autoHide !== false){
35748             this.el.un("mouseout", this.autoHideHd.mouseout);
35749             this.el.un("mouseover", this.autoHideHd.mouseover);
35750         }
35751     },
35752
35753     clearMonitor : function(){
35754         Roo.get(document).un("click", this.slideInIf, this);
35755     },
35756
35757     // these names are backwards but not changed for compat
35758     slideOut : function(){
35759         if(this.isSlid || this.el.hasActiveFx()){
35760             return;
35761         }
35762         this.isSlid = true;
35763         if(this.collapseBtn){
35764             this.collapseBtn.hide();
35765         }
35766         this.closeBtnState = this.closeBtn.getStyle('display');
35767         this.closeBtn.hide();
35768         if(this.stickBtn){
35769             this.stickBtn.show();
35770         }
35771         this.el.show();
35772         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35773         this.beforeSlide();
35774         this.el.setStyle("z-index", 10001);
35775         this.el.slideIn(this.getSlideAnchor(), {
35776             callback: function(){
35777                 this.afterSlide();
35778                 this.initAutoHide();
35779                 Roo.get(document).on("click", this.slideInIf, this);
35780                 this.fireEvent("slideshow", this);
35781             },
35782             scope: this,
35783             block: true
35784         });
35785     },
35786
35787     afterSlideIn : function(){
35788         this.clearAutoHide();
35789         this.isSlid = false;
35790         this.clearMonitor();
35791         this.el.setStyle("z-index", "");
35792         if(this.collapseBtn){
35793             this.collapseBtn.show();
35794         }
35795         this.closeBtn.setStyle('display', this.closeBtnState);
35796         if(this.stickBtn){
35797             this.stickBtn.hide();
35798         }
35799         this.fireEvent("slidehide", this);
35800     },
35801
35802     slideIn : function(cb){
35803         if(!this.isSlid || this.el.hasActiveFx()){
35804             Roo.callback(cb);
35805             return;
35806         }
35807         this.isSlid = false;
35808         this.beforeSlide();
35809         this.el.slideOut(this.getSlideAnchor(), {
35810             callback: function(){
35811                 this.el.setLeftTop(-10000, -10000);
35812                 this.afterSlide();
35813                 this.afterSlideIn();
35814                 Roo.callback(cb);
35815             },
35816             scope: this,
35817             block: true
35818         });
35819     },
35820     
35821     slideInIf : function(e){
35822         if(!e.within(this.el)){
35823             this.slideIn();
35824         }
35825     },
35826
35827     animateCollapse : function(){
35828         this.beforeSlide();
35829         this.el.setStyle("z-index", 20000);
35830         var anchor = this.getSlideAnchor();
35831         this.el.slideOut(anchor, {
35832             callback : function(){
35833                 this.el.setStyle("z-index", "");
35834                 this.collapsedEl.slideIn(anchor, {duration:.3});
35835                 this.afterSlide();
35836                 this.el.setLocation(-10000,-10000);
35837                 this.el.hide();
35838                 this.fireEvent("collapsed", this);
35839             },
35840             scope: this,
35841             block: true
35842         });
35843     },
35844
35845     animateExpand : function(){
35846         this.beforeSlide();
35847         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35848         this.el.setStyle("z-index", 20000);
35849         this.collapsedEl.hide({
35850             duration:.1
35851         });
35852         this.el.slideIn(this.getSlideAnchor(), {
35853             callback : function(){
35854                 this.el.setStyle("z-index", "");
35855                 this.afterSlide();
35856                 if(this.split){
35857                     this.split.el.show();
35858                 }
35859                 this.fireEvent("invalidated", this);
35860                 this.fireEvent("expanded", this);
35861             },
35862             scope: this,
35863             block: true
35864         });
35865     },
35866
35867     anchors : {
35868         "west" : "left",
35869         "east" : "right",
35870         "north" : "top",
35871         "south" : "bottom"
35872     },
35873
35874     sanchors : {
35875         "west" : "l",
35876         "east" : "r",
35877         "north" : "t",
35878         "south" : "b"
35879     },
35880
35881     canchors : {
35882         "west" : "tl-tr",
35883         "east" : "tr-tl",
35884         "north" : "tl-bl",
35885         "south" : "bl-tl"
35886     },
35887
35888     getAnchor : function(){
35889         return this.anchors[this.position];
35890     },
35891
35892     getCollapseAnchor : function(){
35893         return this.canchors[this.position];
35894     },
35895
35896     getSlideAnchor : function(){
35897         return this.sanchors[this.position];
35898     },
35899
35900     getAlignAdj : function(){
35901         var cm = this.cmargins;
35902         switch(this.position){
35903             case "west":
35904                 return [0, 0];
35905             break;
35906             case "east":
35907                 return [0, 0];
35908             break;
35909             case "north":
35910                 return [0, 0];
35911             break;
35912             case "south":
35913                 return [0, 0];
35914             break;
35915         }
35916     },
35917
35918     getExpandAdj : function(){
35919         var c = this.collapsedEl, cm = this.cmargins;
35920         switch(this.position){
35921             case "west":
35922                 return [-(cm.right+c.getWidth()+cm.left), 0];
35923             break;
35924             case "east":
35925                 return [cm.right+c.getWidth()+cm.left, 0];
35926             break;
35927             case "north":
35928                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35929             break;
35930             case "south":
35931                 return [0, cm.top+cm.bottom+c.getHeight()];
35932             break;
35933         }
35934     }
35935 });/*
35936  * Based on:
35937  * Ext JS Library 1.1.1
35938  * Copyright(c) 2006-2007, Ext JS, LLC.
35939  *
35940  * Originally Released Under LGPL - original licence link has changed is not relivant.
35941  *
35942  * Fork - LGPL
35943  * <script type="text/javascript">
35944  */
35945 /*
35946  * These classes are private internal classes
35947  */
35948 Roo.bootstrap.layout.Center = function(config){
35949     config.region = "center";
35950     Roo.bootstrap.layout.Region.call(this, config);
35951     this.visible = true;
35952     this.minWidth = config.minWidth || 20;
35953     this.minHeight = config.minHeight || 20;
35954 };
35955
35956 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35957     hide : function(){
35958         // center panel can't be hidden
35959     },
35960     
35961     show : function(){
35962         // center panel can't be hidden
35963     },
35964     
35965     getMinWidth: function(){
35966         return this.minWidth;
35967     },
35968     
35969     getMinHeight: function(){
35970         return this.minHeight;
35971     }
35972 });
35973
35974
35975
35976
35977  
35978
35979
35980
35981
35982
35983 Roo.bootstrap.layout.North = function(config)
35984 {
35985     config.region = 'north';
35986     config.cursor = 'n-resize';
35987     
35988     Roo.bootstrap.layout.Split.call(this, config);
35989     
35990     
35991     if(this.split){
35992         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35993         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35994         this.split.el.addClass("roo-layout-split-v");
35995     }
35996     var size = config.initialSize || config.height;
35997     if(typeof size != "undefined"){
35998         this.el.setHeight(size);
35999     }
36000 };
36001 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36002 {
36003     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36004     
36005     
36006     
36007     getBox : function(){
36008         if(this.collapsed){
36009             return this.collapsedEl.getBox();
36010         }
36011         var box = this.el.getBox();
36012         if(this.split){
36013             box.height += this.split.el.getHeight();
36014         }
36015         return box;
36016     },
36017     
36018     updateBox : function(box){
36019         if(this.split && !this.collapsed){
36020             box.height -= this.split.el.getHeight();
36021             this.split.el.setLeft(box.x);
36022             this.split.el.setTop(box.y+box.height);
36023             this.split.el.setWidth(box.width);
36024         }
36025         if(this.collapsed){
36026             this.updateBody(box.width, null);
36027         }
36028         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36029     }
36030 });
36031
36032
36033
36034
36035
36036 Roo.bootstrap.layout.South = function(config){
36037     config.region = 'south';
36038     config.cursor = 's-resize';
36039     Roo.bootstrap.layout.Split.call(this, config);
36040     if(this.split){
36041         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36042         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36043         this.split.el.addClass("roo-layout-split-v");
36044     }
36045     var size = config.initialSize || config.height;
36046     if(typeof size != "undefined"){
36047         this.el.setHeight(size);
36048     }
36049 };
36050
36051 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36052     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36053     getBox : function(){
36054         if(this.collapsed){
36055             return this.collapsedEl.getBox();
36056         }
36057         var box = this.el.getBox();
36058         if(this.split){
36059             var sh = this.split.el.getHeight();
36060             box.height += sh;
36061             box.y -= sh;
36062         }
36063         return box;
36064     },
36065     
36066     updateBox : function(box){
36067         if(this.split && !this.collapsed){
36068             var sh = this.split.el.getHeight();
36069             box.height -= sh;
36070             box.y += sh;
36071             this.split.el.setLeft(box.x);
36072             this.split.el.setTop(box.y-sh);
36073             this.split.el.setWidth(box.width);
36074         }
36075         if(this.collapsed){
36076             this.updateBody(box.width, null);
36077         }
36078         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36079     }
36080 });
36081
36082 Roo.bootstrap.layout.East = function(config){
36083     config.region = "east";
36084     config.cursor = "e-resize";
36085     Roo.bootstrap.layout.Split.call(this, config);
36086     if(this.split){
36087         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36088         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36089         this.split.el.addClass("roo-layout-split-h");
36090     }
36091     var size = config.initialSize || config.width;
36092     if(typeof size != "undefined"){
36093         this.el.setWidth(size);
36094     }
36095 };
36096 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36097     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36098     getBox : function(){
36099         if(this.collapsed){
36100             return this.collapsedEl.getBox();
36101         }
36102         var box = this.el.getBox();
36103         if(this.split){
36104             var sw = this.split.el.getWidth();
36105             box.width += sw;
36106             box.x -= sw;
36107         }
36108         return box;
36109     },
36110
36111     updateBox : function(box){
36112         if(this.split && !this.collapsed){
36113             var sw = this.split.el.getWidth();
36114             box.width -= sw;
36115             this.split.el.setLeft(box.x);
36116             this.split.el.setTop(box.y);
36117             this.split.el.setHeight(box.height);
36118             box.x += sw;
36119         }
36120         if(this.collapsed){
36121             this.updateBody(null, box.height);
36122         }
36123         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36124     }
36125 });
36126
36127 Roo.bootstrap.layout.West = function(config){
36128     config.region = "west";
36129     config.cursor = "w-resize";
36130     
36131     Roo.bootstrap.layout.Split.call(this, config);
36132     if(this.split){
36133         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36134         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36135         this.split.el.addClass("roo-layout-split-h");
36136     }
36137     
36138 };
36139 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36140     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36141     
36142     onRender: function(ctr, pos)
36143     {
36144         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36145         var size = this.config.initialSize || this.config.width;
36146         if(typeof size != "undefined"){
36147             this.el.setWidth(size);
36148         }
36149     },
36150     
36151     getBox : function(){
36152         if(this.collapsed){
36153             return this.collapsedEl.getBox();
36154         }
36155         var box = this.el.getBox();
36156         if(this.split){
36157             box.width += this.split.el.getWidth();
36158         }
36159         return box;
36160     },
36161     
36162     updateBox : function(box){
36163         if(this.split && !this.collapsed){
36164             var sw = this.split.el.getWidth();
36165             box.width -= sw;
36166             this.split.el.setLeft(box.x+box.width);
36167             this.split.el.setTop(box.y);
36168             this.split.el.setHeight(box.height);
36169         }
36170         if(this.collapsed){
36171             this.updateBody(null, box.height);
36172         }
36173         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36174     }
36175 });
36176 Roo.namespace("Roo.bootstrap.panel");/*
36177  * Based on:
36178  * Ext JS Library 1.1.1
36179  * Copyright(c) 2006-2007, Ext JS, LLC.
36180  *
36181  * Originally Released Under LGPL - original licence link has changed is not relivant.
36182  *
36183  * Fork - LGPL
36184  * <script type="text/javascript">
36185  */
36186 /**
36187  * @class Roo.ContentPanel
36188  * @extends Roo.util.Observable
36189  * A basic ContentPanel element.
36190  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36191  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36192  * @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
36193  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36194  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36195  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36196  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36197  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36198  * @cfg {String} title          The title for this panel
36199  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36200  * @cfg {String} url            Calls {@link #setUrl} with this value
36201  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36202  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36203  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36204  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36205  * @cfg {Boolean} badges render the badges
36206
36207  * @constructor
36208  * Create a new ContentPanel.
36209  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36210  * @param {String/Object} config A string to set only the title or a config object
36211  * @param {String} content (optional) Set the HTML content for this panel
36212  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36213  */
36214 Roo.bootstrap.panel.Content = function( config){
36215     
36216     this.tpl = config.tpl || false;
36217     
36218     var el = config.el;
36219     var content = config.content;
36220
36221     if(config.autoCreate){ // xtype is available if this is called from factory
36222         el = Roo.id();
36223     }
36224     this.el = Roo.get(el);
36225     if(!this.el && config && config.autoCreate){
36226         if(typeof config.autoCreate == "object"){
36227             if(!config.autoCreate.id){
36228                 config.autoCreate.id = config.id||el;
36229             }
36230             this.el = Roo.DomHelper.append(document.body,
36231                         config.autoCreate, true);
36232         }else{
36233             var elcfg =  {   tag: "div",
36234                             cls: "roo-layout-inactive-content",
36235                             id: config.id||el
36236                             };
36237             if (config.html) {
36238                 elcfg.html = config.html;
36239                 
36240             }
36241                         
36242             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36243         }
36244     } 
36245     this.closable = false;
36246     this.loaded = false;
36247     this.active = false;
36248    
36249       
36250     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36251         
36252         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36253         
36254         this.wrapEl = this.el; //this.el.wrap();
36255         var ti = [];
36256         if (config.toolbar.items) {
36257             ti = config.toolbar.items ;
36258             delete config.toolbar.items ;
36259         }
36260         
36261         var nitems = [];
36262         this.toolbar.render(this.wrapEl, 'before');
36263         for(var i =0;i < ti.length;i++) {
36264           //  Roo.log(['add child', items[i]]);
36265             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36266         }
36267         this.toolbar.items = nitems;
36268         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36269         delete config.toolbar;
36270         
36271     }
36272     /*
36273     // xtype created footer. - not sure if will work as we normally have to render first..
36274     if (this.footer && !this.footer.el && this.footer.xtype) {
36275         if (!this.wrapEl) {
36276             this.wrapEl = this.el.wrap();
36277         }
36278     
36279         this.footer.container = this.wrapEl.createChild();
36280          
36281         this.footer = Roo.factory(this.footer, Roo);
36282         
36283     }
36284     */
36285     
36286      if(typeof config == "string"){
36287         this.title = config;
36288     }else{
36289         Roo.apply(this, config);
36290     }
36291     
36292     if(this.resizeEl){
36293         this.resizeEl = Roo.get(this.resizeEl, true);
36294     }else{
36295         this.resizeEl = this.el;
36296     }
36297     // handle view.xtype
36298     
36299  
36300     
36301     
36302     this.addEvents({
36303         /**
36304          * @event activate
36305          * Fires when this panel is activated. 
36306          * @param {Roo.ContentPanel} this
36307          */
36308         "activate" : true,
36309         /**
36310          * @event deactivate
36311          * Fires when this panel is activated. 
36312          * @param {Roo.ContentPanel} this
36313          */
36314         "deactivate" : true,
36315
36316         /**
36317          * @event resize
36318          * Fires when this panel is resized if fitToFrame is true.
36319          * @param {Roo.ContentPanel} this
36320          * @param {Number} width The width after any component adjustments
36321          * @param {Number} height The height after any component adjustments
36322          */
36323         "resize" : true,
36324         
36325          /**
36326          * @event render
36327          * Fires when this tab is created
36328          * @param {Roo.ContentPanel} this
36329          */
36330         "render" : true
36331         
36332         
36333         
36334     });
36335     
36336
36337     
36338     
36339     if(this.autoScroll){
36340         this.resizeEl.setStyle("overflow", "auto");
36341     } else {
36342         // fix randome scrolling
36343         //this.el.on('scroll', function() {
36344         //    Roo.log('fix random scolling');
36345         //    this.scrollTo('top',0); 
36346         //});
36347     }
36348     content = content || this.content;
36349     if(content){
36350         this.setContent(content);
36351     }
36352     if(config && config.url){
36353         this.setUrl(this.url, this.params, this.loadOnce);
36354     }
36355     
36356     
36357     
36358     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36359     
36360     if (this.view && typeof(this.view.xtype) != 'undefined') {
36361         this.view.el = this.el.appendChild(document.createElement("div"));
36362         this.view = Roo.factory(this.view); 
36363         this.view.render  &&  this.view.render(false, '');  
36364     }
36365     
36366     
36367     this.fireEvent('render', this);
36368 };
36369
36370 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36371     
36372     tabTip : '',
36373     
36374     setRegion : function(region){
36375         this.region = region;
36376         this.setActiveClass(region && !this.background);
36377     },
36378     
36379     
36380     setActiveClass: function(state)
36381     {
36382         if(state){
36383            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36384            this.el.setStyle('position','relative');
36385         }else{
36386            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36387            this.el.setStyle('position', 'absolute');
36388         } 
36389     },
36390     
36391     /**
36392      * Returns the toolbar for this Panel if one was configured. 
36393      * @return {Roo.Toolbar} 
36394      */
36395     getToolbar : function(){
36396         return this.toolbar;
36397     },
36398     
36399     setActiveState : function(active)
36400     {
36401         this.active = active;
36402         this.setActiveClass(active);
36403         if(!active){
36404             this.fireEvent("deactivate", this);
36405         }else{
36406             this.fireEvent("activate", this);
36407         }
36408     },
36409     /**
36410      * Updates this panel's element
36411      * @param {String} content The new content
36412      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36413     */
36414     setContent : function(content, loadScripts){
36415         this.el.update(content, loadScripts);
36416     },
36417
36418     ignoreResize : function(w, h){
36419         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36420             return true;
36421         }else{
36422             this.lastSize = {width: w, height: h};
36423             return false;
36424         }
36425     },
36426     /**
36427      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36428      * @return {Roo.UpdateManager} The UpdateManager
36429      */
36430     getUpdateManager : function(){
36431         return this.el.getUpdateManager();
36432     },
36433      /**
36434      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36435      * @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:
36436 <pre><code>
36437 panel.load({
36438     url: "your-url.php",
36439     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36440     callback: yourFunction,
36441     scope: yourObject, //(optional scope)
36442     discardUrl: false,
36443     nocache: false,
36444     text: "Loading...",
36445     timeout: 30,
36446     scripts: false
36447 });
36448 </code></pre>
36449      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36450      * 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.
36451      * @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}
36452      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36453      * @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.
36454      * @return {Roo.ContentPanel} this
36455      */
36456     load : function(){
36457         var um = this.el.getUpdateManager();
36458         um.update.apply(um, arguments);
36459         return this;
36460     },
36461
36462
36463     /**
36464      * 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.
36465      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36466      * @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)
36467      * @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)
36468      * @return {Roo.UpdateManager} The UpdateManager
36469      */
36470     setUrl : function(url, params, loadOnce){
36471         if(this.refreshDelegate){
36472             this.removeListener("activate", this.refreshDelegate);
36473         }
36474         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36475         this.on("activate", this.refreshDelegate);
36476         return this.el.getUpdateManager();
36477     },
36478     
36479     _handleRefresh : function(url, params, loadOnce){
36480         if(!loadOnce || !this.loaded){
36481             var updater = this.el.getUpdateManager();
36482             updater.update(url, params, this._setLoaded.createDelegate(this));
36483         }
36484     },
36485     
36486     _setLoaded : function(){
36487         this.loaded = true;
36488     }, 
36489     
36490     /**
36491      * Returns this panel's id
36492      * @return {String} 
36493      */
36494     getId : function(){
36495         return this.el.id;
36496     },
36497     
36498     /** 
36499      * Returns this panel's element - used by regiosn to add.
36500      * @return {Roo.Element} 
36501      */
36502     getEl : function(){
36503         return this.wrapEl || this.el;
36504     },
36505     
36506    
36507     
36508     adjustForComponents : function(width, height)
36509     {
36510         //Roo.log('adjustForComponents ');
36511         if(this.resizeEl != this.el){
36512             width -= this.el.getFrameWidth('lr');
36513             height -= this.el.getFrameWidth('tb');
36514         }
36515         if(this.toolbar){
36516             var te = this.toolbar.getEl();
36517             te.setWidth(width);
36518             height -= te.getHeight();
36519         }
36520         if(this.footer){
36521             var te = this.footer.getEl();
36522             te.setWidth(width);
36523             height -= te.getHeight();
36524         }
36525         
36526         
36527         if(this.adjustments){
36528             width += this.adjustments[0];
36529             height += this.adjustments[1];
36530         }
36531         return {"width": width, "height": height};
36532     },
36533     
36534     setSize : function(width, height){
36535         if(this.fitToFrame && !this.ignoreResize(width, height)){
36536             if(this.fitContainer && this.resizeEl != this.el){
36537                 this.el.setSize(width, height);
36538             }
36539             var size = this.adjustForComponents(width, height);
36540             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36541             this.fireEvent('resize', this, size.width, size.height);
36542         }
36543     },
36544     
36545     /**
36546      * Returns this panel's title
36547      * @return {String} 
36548      */
36549     getTitle : function(){
36550         
36551         if (typeof(this.title) != 'object') {
36552             return this.title;
36553         }
36554         
36555         var t = '';
36556         for (var k in this.title) {
36557             if (!this.title.hasOwnProperty(k)) {
36558                 continue;
36559             }
36560             
36561             if (k.indexOf('-') >= 0) {
36562                 var s = k.split('-');
36563                 for (var i = 0; i<s.length; i++) {
36564                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36565                 }
36566             } else {
36567                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36568             }
36569         }
36570         return t;
36571     },
36572     
36573     /**
36574      * Set this panel's title
36575      * @param {String} title
36576      */
36577     setTitle : function(title){
36578         this.title = title;
36579         if(this.region){
36580             this.region.updatePanelTitle(this, title);
36581         }
36582     },
36583     
36584     /**
36585      * Returns true is this panel was configured to be closable
36586      * @return {Boolean} 
36587      */
36588     isClosable : function(){
36589         return this.closable;
36590     },
36591     
36592     beforeSlide : function(){
36593         this.el.clip();
36594         this.resizeEl.clip();
36595     },
36596     
36597     afterSlide : function(){
36598         this.el.unclip();
36599         this.resizeEl.unclip();
36600     },
36601     
36602     /**
36603      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36604      *   Will fail silently if the {@link #setUrl} method has not been called.
36605      *   This does not activate the panel, just updates its content.
36606      */
36607     refresh : function(){
36608         if(this.refreshDelegate){
36609            this.loaded = false;
36610            this.refreshDelegate();
36611         }
36612     },
36613     
36614     /**
36615      * Destroys this panel
36616      */
36617     destroy : function(){
36618         this.el.removeAllListeners();
36619         var tempEl = document.createElement("span");
36620         tempEl.appendChild(this.el.dom);
36621         tempEl.innerHTML = "";
36622         this.el.remove();
36623         this.el = null;
36624     },
36625     
36626     /**
36627      * form - if the content panel contains a form - this is a reference to it.
36628      * @type {Roo.form.Form}
36629      */
36630     form : false,
36631     /**
36632      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36633      *    This contains a reference to it.
36634      * @type {Roo.View}
36635      */
36636     view : false,
36637     
36638       /**
36639      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36640      * <pre><code>
36641
36642 layout.addxtype({
36643        xtype : 'Form',
36644        items: [ .... ]
36645    }
36646 );
36647
36648 </code></pre>
36649      * @param {Object} cfg Xtype definition of item to add.
36650      */
36651     
36652     
36653     getChildContainer: function () {
36654         return this.getEl();
36655     }
36656     
36657     
36658     /*
36659         var  ret = new Roo.factory(cfg);
36660         return ret;
36661         
36662         
36663         // add form..
36664         if (cfg.xtype.match(/^Form$/)) {
36665             
36666             var el;
36667             //if (this.footer) {
36668             //    el = this.footer.container.insertSibling(false, 'before');
36669             //} else {
36670                 el = this.el.createChild();
36671             //}
36672
36673             this.form = new  Roo.form.Form(cfg);
36674             
36675             
36676             if ( this.form.allItems.length) {
36677                 this.form.render(el.dom);
36678             }
36679             return this.form;
36680         }
36681         // should only have one of theses..
36682         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36683             // views.. should not be just added - used named prop 'view''
36684             
36685             cfg.el = this.el.appendChild(document.createElement("div"));
36686             // factory?
36687             
36688             var ret = new Roo.factory(cfg);
36689              
36690              ret.render && ret.render(false, ''); // render blank..
36691             this.view = ret;
36692             return ret;
36693         }
36694         return false;
36695     }
36696     \*/
36697 });
36698  
36699 /**
36700  * @class Roo.bootstrap.panel.Grid
36701  * @extends Roo.bootstrap.panel.Content
36702  * @constructor
36703  * Create a new GridPanel.
36704  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36705  * @param {Object} config A the config object
36706   
36707  */
36708
36709
36710
36711 Roo.bootstrap.panel.Grid = function(config)
36712 {
36713     
36714       
36715     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36716         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36717
36718     config.el = this.wrapper;
36719     //this.el = this.wrapper;
36720     
36721       if (config.container) {
36722         // ctor'ed from a Border/panel.grid
36723         
36724         
36725         this.wrapper.setStyle("overflow", "hidden");
36726         this.wrapper.addClass('roo-grid-container');
36727
36728     }
36729     
36730     
36731     if(config.toolbar){
36732         var tool_el = this.wrapper.createChild();    
36733         this.toolbar = Roo.factory(config.toolbar);
36734         var ti = [];
36735         if (config.toolbar.items) {
36736             ti = config.toolbar.items ;
36737             delete config.toolbar.items ;
36738         }
36739         
36740         var nitems = [];
36741         this.toolbar.render(tool_el);
36742         for(var i =0;i < ti.length;i++) {
36743           //  Roo.log(['add child', items[i]]);
36744             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36745         }
36746         this.toolbar.items = nitems;
36747         
36748         delete config.toolbar;
36749     }
36750     
36751     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36752     config.grid.scrollBody = true;;
36753     config.grid.monitorWindowResize = false; // turn off autosizing
36754     config.grid.autoHeight = false;
36755     config.grid.autoWidth = false;
36756     
36757     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36758     
36759     if (config.background) {
36760         // render grid on panel activation (if panel background)
36761         this.on('activate', function(gp) {
36762             if (!gp.grid.rendered) {
36763                 gp.grid.render(this.wrapper);
36764                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36765             }
36766         });
36767             
36768     } else {
36769         this.grid.render(this.wrapper);
36770         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36771
36772     }
36773     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36774     // ??? needed ??? config.el = this.wrapper;
36775     
36776     
36777     
36778   
36779     // xtype created footer. - not sure if will work as we normally have to render first..
36780     if (this.footer && !this.footer.el && this.footer.xtype) {
36781         
36782         var ctr = this.grid.getView().getFooterPanel(true);
36783         this.footer.dataSource = this.grid.dataSource;
36784         this.footer = Roo.factory(this.footer, Roo);
36785         this.footer.render(ctr);
36786         
36787     }
36788     
36789     
36790     
36791     
36792      
36793 };
36794
36795 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36796     getId : function(){
36797         return this.grid.id;
36798     },
36799     
36800     /**
36801      * Returns the grid for this panel
36802      * @return {Roo.bootstrap.Table} 
36803      */
36804     getGrid : function(){
36805         return this.grid;    
36806     },
36807     
36808     setSize : function(width, height){
36809         if(!this.ignoreResize(width, height)){
36810             var grid = this.grid;
36811             var size = this.adjustForComponents(width, height);
36812             var gridel = grid.getGridEl();
36813             gridel.setSize(size.width, size.height);
36814             /*
36815             var thd = grid.getGridEl().select('thead',true).first();
36816             var tbd = grid.getGridEl().select('tbody', true).first();
36817             if (tbd) {
36818                 tbd.setSize(width, height - thd.getHeight());
36819             }
36820             */
36821             grid.autoSize();
36822         }
36823     },
36824      
36825     
36826     
36827     beforeSlide : function(){
36828         this.grid.getView().scroller.clip();
36829     },
36830     
36831     afterSlide : function(){
36832         this.grid.getView().scroller.unclip();
36833     },
36834     
36835     destroy : function(){
36836         this.grid.destroy();
36837         delete this.grid;
36838         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36839     }
36840 });
36841
36842 /**
36843  * @class Roo.bootstrap.panel.Nest
36844  * @extends Roo.bootstrap.panel.Content
36845  * @constructor
36846  * Create a new Panel, that can contain a layout.Border.
36847  * 
36848  * 
36849  * @param {Roo.BorderLayout} layout The layout for this panel
36850  * @param {String/Object} config A string to set only the title or a config object
36851  */
36852 Roo.bootstrap.panel.Nest = function(config)
36853 {
36854     // construct with only one argument..
36855     /* FIXME - implement nicer consturctors
36856     if (layout.layout) {
36857         config = layout;
36858         layout = config.layout;
36859         delete config.layout;
36860     }
36861     if (layout.xtype && !layout.getEl) {
36862         // then layout needs constructing..
36863         layout = Roo.factory(layout, Roo);
36864     }
36865     */
36866     
36867     config.el =  config.layout.getEl();
36868     
36869     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36870     
36871     config.layout.monitorWindowResize = false; // turn off autosizing
36872     this.layout = config.layout;
36873     this.layout.getEl().addClass("roo-layout-nested-layout");
36874     
36875     
36876     
36877     
36878 };
36879
36880 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36881
36882     setSize : function(width, height){
36883         if(!this.ignoreResize(width, height)){
36884             var size = this.adjustForComponents(width, height);
36885             var el = this.layout.getEl();
36886             if (size.height < 1) {
36887                 el.setWidth(size.width);   
36888             } else {
36889                 el.setSize(size.width, size.height);
36890             }
36891             var touch = el.dom.offsetWidth;
36892             this.layout.layout();
36893             // ie requires a double layout on the first pass
36894             if(Roo.isIE && !this.initialized){
36895                 this.initialized = true;
36896                 this.layout.layout();
36897             }
36898         }
36899     },
36900     
36901     // activate all subpanels if not currently active..
36902     
36903     setActiveState : function(active){
36904         this.active = active;
36905         this.setActiveClass(active);
36906         
36907         if(!active){
36908             this.fireEvent("deactivate", this);
36909             return;
36910         }
36911         
36912         this.fireEvent("activate", this);
36913         // not sure if this should happen before or after..
36914         if (!this.layout) {
36915             return; // should not happen..
36916         }
36917         var reg = false;
36918         for (var r in this.layout.regions) {
36919             reg = this.layout.getRegion(r);
36920             if (reg.getActivePanel()) {
36921                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36922                 reg.setActivePanel(reg.getActivePanel());
36923                 continue;
36924             }
36925             if (!reg.panels.length) {
36926                 continue;
36927             }
36928             reg.showPanel(reg.getPanel(0));
36929         }
36930         
36931         
36932         
36933         
36934     },
36935     
36936     /**
36937      * Returns the nested BorderLayout for this panel
36938      * @return {Roo.BorderLayout} 
36939      */
36940     getLayout : function(){
36941         return this.layout;
36942     },
36943     
36944      /**
36945      * Adds a xtype elements to the layout of the nested panel
36946      * <pre><code>
36947
36948 panel.addxtype({
36949        xtype : 'ContentPanel',
36950        region: 'west',
36951        items: [ .... ]
36952    }
36953 );
36954
36955 panel.addxtype({
36956         xtype : 'NestedLayoutPanel',
36957         region: 'west',
36958         layout: {
36959            center: { },
36960            west: { }   
36961         },
36962         items : [ ... list of content panels or nested layout panels.. ]
36963    }
36964 );
36965 </code></pre>
36966      * @param {Object} cfg Xtype definition of item to add.
36967      */
36968     addxtype : function(cfg) {
36969         return this.layout.addxtype(cfg);
36970     
36971     }
36972 });        /*
36973  * Based on:
36974  * Ext JS Library 1.1.1
36975  * Copyright(c) 2006-2007, Ext JS, LLC.
36976  *
36977  * Originally Released Under LGPL - original licence link has changed is not relivant.
36978  *
36979  * Fork - LGPL
36980  * <script type="text/javascript">
36981  */
36982 /**
36983  * @class Roo.TabPanel
36984  * @extends Roo.util.Observable
36985  * A lightweight tab container.
36986  * <br><br>
36987  * Usage:
36988  * <pre><code>
36989 // basic tabs 1, built from existing content
36990 var tabs = new Roo.TabPanel("tabs1");
36991 tabs.addTab("script", "View Script");
36992 tabs.addTab("markup", "View Markup");
36993 tabs.activate("script");
36994
36995 // more advanced tabs, built from javascript
36996 var jtabs = new Roo.TabPanel("jtabs");
36997 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36998
36999 // set up the UpdateManager
37000 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37001 var updater = tab2.getUpdateManager();
37002 updater.setDefaultUrl("ajax1.htm");
37003 tab2.on('activate', updater.refresh, updater, true);
37004
37005 // Use setUrl for Ajax loading
37006 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37007 tab3.setUrl("ajax2.htm", null, true);
37008
37009 // Disabled tab
37010 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37011 tab4.disable();
37012
37013 jtabs.activate("jtabs-1");
37014  * </code></pre>
37015  * @constructor
37016  * Create a new TabPanel.
37017  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37018  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37019  */
37020 Roo.bootstrap.panel.Tabs = function(config){
37021     /**
37022     * The container element for this TabPanel.
37023     * @type Roo.Element
37024     */
37025     this.el = Roo.get(config.el);
37026     delete config.el;
37027     if(config){
37028         if(typeof config == "boolean"){
37029             this.tabPosition = config ? "bottom" : "top";
37030         }else{
37031             Roo.apply(this, config);
37032         }
37033     }
37034     
37035     if(this.tabPosition == "bottom"){
37036         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37037         this.el.addClass("roo-tabs-bottom");
37038     }
37039     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37040     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37041     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37042     if(Roo.isIE){
37043         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37044     }
37045     if(this.tabPosition != "bottom"){
37046         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37047          * @type Roo.Element
37048          */
37049         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37050         this.el.addClass("roo-tabs-top");
37051     }
37052     this.items = [];
37053
37054     this.bodyEl.setStyle("position", "relative");
37055
37056     this.active = null;
37057     this.activateDelegate = this.activate.createDelegate(this);
37058
37059     this.addEvents({
37060         /**
37061          * @event tabchange
37062          * Fires when the active tab changes
37063          * @param {Roo.TabPanel} this
37064          * @param {Roo.TabPanelItem} activePanel The new active tab
37065          */
37066         "tabchange": true,
37067         /**
37068          * @event beforetabchange
37069          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37070          * @param {Roo.TabPanel} this
37071          * @param {Object} e Set cancel to true on this object to cancel the tab change
37072          * @param {Roo.TabPanelItem} tab The tab being changed to
37073          */
37074         "beforetabchange" : true
37075     });
37076
37077     Roo.EventManager.onWindowResize(this.onResize, this);
37078     this.cpad = this.el.getPadding("lr");
37079     this.hiddenCount = 0;
37080
37081
37082     // toolbar on the tabbar support...
37083     if (this.toolbar) {
37084         alert("no toolbar support yet");
37085         this.toolbar  = false;
37086         /*
37087         var tcfg = this.toolbar;
37088         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37089         this.toolbar = new Roo.Toolbar(tcfg);
37090         if (Roo.isSafari) {
37091             var tbl = tcfg.container.child('table', true);
37092             tbl.setAttribute('width', '100%');
37093         }
37094         */
37095         
37096     }
37097    
37098
37099
37100     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37101 };
37102
37103 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37104     /*
37105      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37106      */
37107     tabPosition : "top",
37108     /*
37109      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37110      */
37111     currentTabWidth : 0,
37112     /*
37113      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37114      */
37115     minTabWidth : 40,
37116     /*
37117      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37118      */
37119     maxTabWidth : 250,
37120     /*
37121      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37122      */
37123     preferredTabWidth : 175,
37124     /*
37125      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37126      */
37127     resizeTabs : false,
37128     /*
37129      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37130      */
37131     monitorResize : true,
37132     /*
37133      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37134      */
37135     toolbar : false,
37136
37137     /**
37138      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37139      * @param {String} id The id of the div to use <b>or create</b>
37140      * @param {String} text The text for the tab
37141      * @param {String} content (optional) Content to put in the TabPanelItem body
37142      * @param {Boolean} closable (optional) True to create a close icon on the tab
37143      * @return {Roo.TabPanelItem} The created TabPanelItem
37144      */
37145     addTab : function(id, text, content, closable, tpl)
37146     {
37147         var item = new Roo.bootstrap.panel.TabItem({
37148             panel: this,
37149             id : id,
37150             text : text,
37151             closable : closable,
37152             tpl : tpl
37153         });
37154         this.addTabItem(item);
37155         if(content){
37156             item.setContent(content);
37157         }
37158         return item;
37159     },
37160
37161     /**
37162      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37163      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37164      * @return {Roo.TabPanelItem}
37165      */
37166     getTab : function(id){
37167         return this.items[id];
37168     },
37169
37170     /**
37171      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37172      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37173      */
37174     hideTab : function(id){
37175         var t = this.items[id];
37176         if(!t.isHidden()){
37177            t.setHidden(true);
37178            this.hiddenCount++;
37179            this.autoSizeTabs();
37180         }
37181     },
37182
37183     /**
37184      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37185      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37186      */
37187     unhideTab : function(id){
37188         var t = this.items[id];
37189         if(t.isHidden()){
37190            t.setHidden(false);
37191            this.hiddenCount--;
37192            this.autoSizeTabs();
37193         }
37194     },
37195
37196     /**
37197      * Adds an existing {@link Roo.TabPanelItem}.
37198      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37199      */
37200     addTabItem : function(item){
37201         this.items[item.id] = item;
37202         this.items.push(item);
37203       //  if(this.resizeTabs){
37204     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37205   //         this.autoSizeTabs();
37206 //        }else{
37207 //            item.autoSize();
37208        // }
37209     },
37210
37211     /**
37212      * Removes a {@link Roo.TabPanelItem}.
37213      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37214      */
37215     removeTab : function(id){
37216         var items = this.items;
37217         var tab = items[id];
37218         if(!tab) { return; }
37219         var index = items.indexOf(tab);
37220         if(this.active == tab && items.length > 1){
37221             var newTab = this.getNextAvailable(index);
37222             if(newTab) {
37223                 newTab.activate();
37224             }
37225         }
37226         this.stripEl.dom.removeChild(tab.pnode.dom);
37227         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37228             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37229         }
37230         items.splice(index, 1);
37231         delete this.items[tab.id];
37232         tab.fireEvent("close", tab);
37233         tab.purgeListeners();
37234         this.autoSizeTabs();
37235     },
37236
37237     getNextAvailable : function(start){
37238         var items = this.items;
37239         var index = start;
37240         // look for a next tab that will slide over to
37241         // replace the one being removed
37242         while(index < items.length){
37243             var item = items[++index];
37244             if(item && !item.isHidden()){
37245                 return item;
37246             }
37247         }
37248         // if one isn't found select the previous tab (on the left)
37249         index = start;
37250         while(index >= 0){
37251             var item = items[--index];
37252             if(item && !item.isHidden()){
37253                 return item;
37254             }
37255         }
37256         return null;
37257     },
37258
37259     /**
37260      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37261      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37262      */
37263     disableTab : function(id){
37264         var tab = this.items[id];
37265         if(tab && this.active != tab){
37266             tab.disable();
37267         }
37268     },
37269
37270     /**
37271      * Enables a {@link Roo.TabPanelItem} that is disabled.
37272      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37273      */
37274     enableTab : function(id){
37275         var tab = this.items[id];
37276         tab.enable();
37277     },
37278
37279     /**
37280      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37281      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37282      * @return {Roo.TabPanelItem} The TabPanelItem.
37283      */
37284     activate : function(id){
37285         var tab = this.items[id];
37286         if(!tab){
37287             return null;
37288         }
37289         if(tab == this.active || tab.disabled){
37290             return tab;
37291         }
37292         var e = {};
37293         this.fireEvent("beforetabchange", this, e, tab);
37294         if(e.cancel !== true && !tab.disabled){
37295             if(this.active){
37296                 this.active.hide();
37297             }
37298             this.active = this.items[id];
37299             this.active.show();
37300             this.fireEvent("tabchange", this, this.active);
37301         }
37302         return tab;
37303     },
37304
37305     /**
37306      * Gets the active {@link Roo.TabPanelItem}.
37307      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37308      */
37309     getActiveTab : function(){
37310         return this.active;
37311     },
37312
37313     /**
37314      * Updates the tab body element to fit the height of the container element
37315      * for overflow scrolling
37316      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37317      */
37318     syncHeight : function(targetHeight){
37319         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37320         var bm = this.bodyEl.getMargins();
37321         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37322         this.bodyEl.setHeight(newHeight);
37323         return newHeight;
37324     },
37325
37326     onResize : function(){
37327         if(this.monitorResize){
37328             this.autoSizeTabs();
37329         }
37330     },
37331
37332     /**
37333      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37334      */
37335     beginUpdate : function(){
37336         this.updating = true;
37337     },
37338
37339     /**
37340      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37341      */
37342     endUpdate : function(){
37343         this.updating = false;
37344         this.autoSizeTabs();
37345     },
37346
37347     /**
37348      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37349      */
37350     autoSizeTabs : function(){
37351         var count = this.items.length;
37352         var vcount = count - this.hiddenCount;
37353         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37354             return;
37355         }
37356         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37357         var availWidth = Math.floor(w / vcount);
37358         var b = this.stripBody;
37359         if(b.getWidth() > w){
37360             var tabs = this.items;
37361             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37362             if(availWidth < this.minTabWidth){
37363                 /*if(!this.sleft){    // incomplete scrolling code
37364                     this.createScrollButtons();
37365                 }
37366                 this.showScroll();
37367                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37368             }
37369         }else{
37370             if(this.currentTabWidth < this.preferredTabWidth){
37371                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37372             }
37373         }
37374     },
37375
37376     /**
37377      * Returns the number of tabs in this TabPanel.
37378      * @return {Number}
37379      */
37380      getCount : function(){
37381          return this.items.length;
37382      },
37383
37384     /**
37385      * Resizes all the tabs to the passed width
37386      * @param {Number} The new width
37387      */
37388     setTabWidth : function(width){
37389         this.currentTabWidth = width;
37390         for(var i = 0, len = this.items.length; i < len; i++) {
37391                 if(!this.items[i].isHidden()) {
37392                 this.items[i].setWidth(width);
37393             }
37394         }
37395     },
37396
37397     /**
37398      * Destroys this TabPanel
37399      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37400      */
37401     destroy : function(removeEl){
37402         Roo.EventManager.removeResizeListener(this.onResize, this);
37403         for(var i = 0, len = this.items.length; i < len; i++){
37404             this.items[i].purgeListeners();
37405         }
37406         if(removeEl === true){
37407             this.el.update("");
37408             this.el.remove();
37409         }
37410     },
37411     
37412     createStrip : function(container)
37413     {
37414         var strip = document.createElement("nav");
37415         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37416         container.appendChild(strip);
37417         return strip;
37418     },
37419     
37420     createStripList : function(strip)
37421     {
37422         // div wrapper for retard IE
37423         // returns the "tr" element.
37424         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37425         //'<div class="x-tabs-strip-wrap">'+
37426           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37427           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37428         return strip.firstChild; //.firstChild.firstChild.firstChild;
37429     },
37430     createBody : function(container)
37431     {
37432         var body = document.createElement("div");
37433         Roo.id(body, "tab-body");
37434         //Roo.fly(body).addClass("x-tabs-body");
37435         Roo.fly(body).addClass("tab-content");
37436         container.appendChild(body);
37437         return body;
37438     },
37439     createItemBody :function(bodyEl, id){
37440         var body = Roo.getDom(id);
37441         if(!body){
37442             body = document.createElement("div");
37443             body.id = id;
37444         }
37445         //Roo.fly(body).addClass("x-tabs-item-body");
37446         Roo.fly(body).addClass("tab-pane");
37447          bodyEl.insertBefore(body, bodyEl.firstChild);
37448         return body;
37449     },
37450     /** @private */
37451     createStripElements :  function(stripEl, text, closable, tpl)
37452     {
37453         var td = document.createElement("li"); // was td..
37454         
37455         
37456         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37457         
37458         
37459         stripEl.appendChild(td);
37460         /*if(closable){
37461             td.className = "x-tabs-closable";
37462             if(!this.closeTpl){
37463                 this.closeTpl = new Roo.Template(
37464                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37465                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37466                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37467                 );
37468             }
37469             var el = this.closeTpl.overwrite(td, {"text": text});
37470             var close = el.getElementsByTagName("div")[0];
37471             var inner = el.getElementsByTagName("em")[0];
37472             return {"el": el, "close": close, "inner": inner};
37473         } else {
37474         */
37475         // not sure what this is..
37476 //            if(!this.tabTpl){
37477                 //this.tabTpl = new Roo.Template(
37478                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37479                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37480                 //);
37481 //                this.tabTpl = new Roo.Template(
37482 //                   '<a href="#">' +
37483 //                   '<span unselectable="on"' +
37484 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37485 //                            ' >{text}</span></a>'
37486 //                );
37487 //                
37488 //            }
37489
37490
37491             var template = tpl || this.tabTpl || false;
37492             
37493             if(!template){
37494                 
37495                 template = new Roo.Template(
37496                    '<a href="#">' +
37497                    '<span unselectable="on"' +
37498                             (this.disableTooltips ? '' : ' title="{text}"') +
37499                             ' >{text}</span></a>'
37500                 );
37501             }
37502             
37503             switch (typeof(template)) {
37504                 case 'object' :
37505                     break;
37506                 case 'string' :
37507                     template = new Roo.Template(template);
37508                     break;
37509                 default :
37510                     break;
37511             }
37512             
37513             var el = template.overwrite(td, {"text": text});
37514             
37515             var inner = el.getElementsByTagName("span")[0];
37516             
37517             return {"el": el, "inner": inner};
37518             
37519     }
37520         
37521     
37522 });
37523
37524 /**
37525  * @class Roo.TabPanelItem
37526  * @extends Roo.util.Observable
37527  * Represents an individual item (tab plus body) in a TabPanel.
37528  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37529  * @param {String} id The id of this TabPanelItem
37530  * @param {String} text The text for the tab of this TabPanelItem
37531  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37532  */
37533 Roo.bootstrap.panel.TabItem = function(config){
37534     /**
37535      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37536      * @type Roo.TabPanel
37537      */
37538     this.tabPanel = config.panel;
37539     /**
37540      * The id for this TabPanelItem
37541      * @type String
37542      */
37543     this.id = config.id;
37544     /** @private */
37545     this.disabled = false;
37546     /** @private */
37547     this.text = config.text;
37548     /** @private */
37549     this.loaded = false;
37550     this.closable = config.closable;
37551
37552     /**
37553      * The body element for this TabPanelItem.
37554      * @type Roo.Element
37555      */
37556     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37557     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37558     this.bodyEl.setStyle("display", "block");
37559     this.bodyEl.setStyle("zoom", "1");
37560     //this.hideAction();
37561
37562     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37563     /** @private */
37564     this.el = Roo.get(els.el);
37565     this.inner = Roo.get(els.inner, true);
37566     this.textEl = Roo.get(this.el.dom.firstChild, true);
37567     this.pnode = Roo.get(els.el.parentNode, true);
37568     this.el.on("mousedown", this.onTabMouseDown, this);
37569     this.el.on("click", this.onTabClick, this);
37570     /** @private */
37571     if(config.closable){
37572         var c = Roo.get(els.close, true);
37573         c.dom.title = this.closeText;
37574         c.addClassOnOver("close-over");
37575         c.on("click", this.closeClick, this);
37576      }
37577
37578     this.addEvents({
37579          /**
37580          * @event activate
37581          * Fires when this tab becomes the active tab.
37582          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37583          * @param {Roo.TabPanelItem} this
37584          */
37585         "activate": true,
37586         /**
37587          * @event beforeclose
37588          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37589          * @param {Roo.TabPanelItem} this
37590          * @param {Object} e Set cancel to true on this object to cancel the close.
37591          */
37592         "beforeclose": true,
37593         /**
37594          * @event close
37595          * Fires when this tab is closed.
37596          * @param {Roo.TabPanelItem} this
37597          */
37598          "close": true,
37599         /**
37600          * @event deactivate
37601          * Fires when this tab is no longer the active tab.
37602          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37603          * @param {Roo.TabPanelItem} this
37604          */
37605          "deactivate" : true
37606     });
37607     this.hidden = false;
37608
37609     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37610 };
37611
37612 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37613            {
37614     purgeListeners : function(){
37615        Roo.util.Observable.prototype.purgeListeners.call(this);
37616        this.el.removeAllListeners();
37617     },
37618     /**
37619      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37620      */
37621     show : function(){
37622         this.pnode.addClass("active");
37623         this.showAction();
37624         if(Roo.isOpera){
37625             this.tabPanel.stripWrap.repaint();
37626         }
37627         this.fireEvent("activate", this.tabPanel, this);
37628     },
37629
37630     /**
37631      * Returns true if this tab is the active tab.
37632      * @return {Boolean}
37633      */
37634     isActive : function(){
37635         return this.tabPanel.getActiveTab() == this;
37636     },
37637
37638     /**
37639      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37640      */
37641     hide : function(){
37642         this.pnode.removeClass("active");
37643         this.hideAction();
37644         this.fireEvent("deactivate", this.tabPanel, this);
37645     },
37646
37647     hideAction : function(){
37648         this.bodyEl.hide();
37649         this.bodyEl.setStyle("position", "absolute");
37650         this.bodyEl.setLeft("-20000px");
37651         this.bodyEl.setTop("-20000px");
37652     },
37653
37654     showAction : function(){
37655         this.bodyEl.setStyle("position", "relative");
37656         this.bodyEl.setTop("");
37657         this.bodyEl.setLeft("");
37658         this.bodyEl.show();
37659     },
37660
37661     /**
37662      * Set the tooltip for the tab.
37663      * @param {String} tooltip The tab's tooltip
37664      */
37665     setTooltip : function(text){
37666         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37667             this.textEl.dom.qtip = text;
37668             this.textEl.dom.removeAttribute('title');
37669         }else{
37670             this.textEl.dom.title = text;
37671         }
37672     },
37673
37674     onTabClick : function(e){
37675         e.preventDefault();
37676         this.tabPanel.activate(this.id);
37677     },
37678
37679     onTabMouseDown : function(e){
37680         e.preventDefault();
37681         this.tabPanel.activate(this.id);
37682     },
37683 /*
37684     getWidth : function(){
37685         return this.inner.getWidth();
37686     },
37687
37688     setWidth : function(width){
37689         var iwidth = width - this.pnode.getPadding("lr");
37690         this.inner.setWidth(iwidth);
37691         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37692         this.pnode.setWidth(width);
37693     },
37694 */
37695     /**
37696      * Show or hide the tab
37697      * @param {Boolean} hidden True to hide or false to show.
37698      */
37699     setHidden : function(hidden){
37700         this.hidden = hidden;
37701         this.pnode.setStyle("display", hidden ? "none" : "");
37702     },
37703
37704     /**
37705      * Returns true if this tab is "hidden"
37706      * @return {Boolean}
37707      */
37708     isHidden : function(){
37709         return this.hidden;
37710     },
37711
37712     /**
37713      * Returns the text for this tab
37714      * @return {String}
37715      */
37716     getText : function(){
37717         return this.text;
37718     },
37719     /*
37720     autoSize : function(){
37721         //this.el.beginMeasure();
37722         this.textEl.setWidth(1);
37723         /*
37724          *  #2804 [new] Tabs in Roojs
37725          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37726          */
37727         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37728         //this.el.endMeasure();
37729     //},
37730
37731     /**
37732      * Sets the text for the tab (Note: this also sets the tooltip text)
37733      * @param {String} text The tab's text and tooltip
37734      */
37735     setText : function(text){
37736         this.text = text;
37737         this.textEl.update(text);
37738         this.setTooltip(text);
37739         //if(!this.tabPanel.resizeTabs){
37740         //    this.autoSize();
37741         //}
37742     },
37743     /**
37744      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37745      */
37746     activate : function(){
37747         this.tabPanel.activate(this.id);
37748     },
37749
37750     /**
37751      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37752      */
37753     disable : function(){
37754         if(this.tabPanel.active != this){
37755             this.disabled = true;
37756             this.pnode.addClass("disabled");
37757         }
37758     },
37759
37760     /**
37761      * Enables this TabPanelItem if it was previously disabled.
37762      */
37763     enable : function(){
37764         this.disabled = false;
37765         this.pnode.removeClass("disabled");
37766     },
37767
37768     /**
37769      * Sets the content for this TabPanelItem.
37770      * @param {String} content The content
37771      * @param {Boolean} loadScripts true to look for and load scripts
37772      */
37773     setContent : function(content, loadScripts){
37774         this.bodyEl.update(content, loadScripts);
37775     },
37776
37777     /**
37778      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37779      * @return {Roo.UpdateManager} The UpdateManager
37780      */
37781     getUpdateManager : function(){
37782         return this.bodyEl.getUpdateManager();
37783     },
37784
37785     /**
37786      * Set a URL to be used to load the content for this TabPanelItem.
37787      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37788      * @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)
37789      * @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)
37790      * @return {Roo.UpdateManager} The UpdateManager
37791      */
37792     setUrl : function(url, params, loadOnce){
37793         if(this.refreshDelegate){
37794             this.un('activate', this.refreshDelegate);
37795         }
37796         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37797         this.on("activate", this.refreshDelegate);
37798         return this.bodyEl.getUpdateManager();
37799     },
37800
37801     /** @private */
37802     _handleRefresh : function(url, params, loadOnce){
37803         if(!loadOnce || !this.loaded){
37804             var updater = this.bodyEl.getUpdateManager();
37805             updater.update(url, params, this._setLoaded.createDelegate(this));
37806         }
37807     },
37808
37809     /**
37810      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37811      *   Will fail silently if the setUrl method has not been called.
37812      *   This does not activate the panel, just updates its content.
37813      */
37814     refresh : function(){
37815         if(this.refreshDelegate){
37816            this.loaded = false;
37817            this.refreshDelegate();
37818         }
37819     },
37820
37821     /** @private */
37822     _setLoaded : function(){
37823         this.loaded = true;
37824     },
37825
37826     /** @private */
37827     closeClick : function(e){
37828         var o = {};
37829         e.stopEvent();
37830         this.fireEvent("beforeclose", this, o);
37831         if(o.cancel !== true){
37832             this.tabPanel.removeTab(this.id);
37833         }
37834     },
37835     /**
37836      * The text displayed in the tooltip for the close icon.
37837      * @type String
37838      */
37839     closeText : "Close this tab"
37840 });
37841 /*
37842  * - LGPL
37843  *
37844  * element
37845  * 
37846  */
37847
37848 /**
37849  * @class Roo.bootstrap.PhoneInput
37850  * @extends Roo.bootstrap.TriggerField
37851  * Bootstrap PhoneInput class
37852  * 
37853  * @constructor
37854  * Create a new PhoneInput
37855  * @param {Object} config The config object
37856 */
37857
37858 Roo.bootstrap.PhoneInput = function(config){
37859     
37860     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37861     
37862     this.addEvents({
37863         /**
37864          * @event expand
37865          * Fires when the dropdown list is expanded
37866              * @param {Roo.bootstrap.ComboBox} combo This combo box
37867              */
37868         'expand' : true,
37869         /**
37870          * @event collapse
37871          * Fires when the dropdown list is collapsed
37872              * @param {Roo.bootstrap.ComboBox} combo This combo box
37873              */
37874         'collapse' : true,
37875         /**
37876          * @event beforeselect
37877          * Fires before a list item is selected. Return false to cancel the selection.
37878              * @param {Roo.bootstrap.ComboBox} combo This combo box
37879              * @param {Roo.data.Record} record The data record returned from the underlying store
37880              * @param {Number} index The index of the selected item in the dropdown list
37881              */
37882         'beforeselect' : true,
37883         /**
37884          * @event select
37885          * Fires when a list item is selected
37886              * @param {Roo.bootstrap.ComboBox} combo This combo box
37887              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37888              * @param {Number} index The index of the selected item in the dropdown list
37889              */
37890         'select' : true,
37891         /**
37892          * @event beforequery
37893          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37894          * The event object passed has these properties:
37895              * @param {Roo.bootstrap.ComboBox} combo This combo box
37896              * @param {String} query The query
37897              * @param {Boolean} forceAll true to force "all" query
37898              * @param {Boolean} cancel true to cancel the query
37899              * @param {Object} e The query event object
37900              */
37901         'beforequery': true,
37902          /**
37903          * @event add
37904          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37905              * @param {Roo.bootstrap.ComboBox} combo This combo box
37906              */
37907         'add' : true,
37908         /**
37909          * @event edit
37910          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37911              * @param {Roo.bootstrap.ComboBox} combo This combo box
37912              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37913              */
37914         'edit' : true,
37915         /**
37916          * @event remove
37917          * Fires when the remove value from the combobox array
37918              * @param {Roo.bootstrap.ComboBox} combo This combo box
37919              */
37920         'remove' : true,
37921         /**
37922          * @event afterremove
37923          * Fires when the remove value from the combobox array
37924              * @param {Roo.bootstrap.ComboBox} combo This combo box
37925              */
37926         'afterremove' : true,
37927         /**
37928          * @event specialfilter
37929          * Fires when specialfilter
37930             * @param {Roo.bootstrap.ComboBox} combo This combo box
37931             */
37932         'touchviewdisplay' : true
37933     });
37934     
37935     this.country = []; //fetch country JSON
37936 };
37937
37938 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37939      
37940      listWidth: undefined,
37941      
37942      modalTitle : '', 
37943      
37944      selectedClass: 'active',
37945      
37946      maxHeight: 300,
37947      
37948      minListWidth : 70,
37949      
37950      triggerAction: 'query',
37951      
37952      validClass : "has-success",
37953      
37954      invalidClass: "has-warning",
37955      
37956      //new settings
37957      defaultCountry: 'hk',
37958      
37959      preferedCountries: undefined, //array
37960      
37961      filterCountries: undefined, //array
37962      
37963      displayMode: undefined, //string
37964      
37965      getAutoCreate : function(){
37966     
37967          this.list = Roo.bootstrap.PhoneInput.List;
37968     
37969         if(this.filterCountries) {
37970             for(var i = 0; i < this.filterCountries.length; i++) {
37971                 delete this.list[this.filterCountries[i]];
37972             }
37973         }
37974         
37975         if (this.preferedCountries) {
37976             //another list??
37977         }
37978         
37979          var align = this.labelAlign || this.parentLabelAlign();
37980          
37981          var id = Roo.id(); //all el??
37982          
37983          var cfg = {
37984              cls: 'form-group'
37985          };
37986          
37987          var input =  {
37988              tag: 'input',
37989              id : id,
37990              type : this.inputType,
37991              cls : 'form-control',
37992              style: 'padding-left: 60px;',
37993              placeholder : this.placeholder || ''
37994          };
37995          
37996          if (this.name) {
37997              input.name = this.name;
37998          }
37999          if (this.size) {
38000              input.cls += ' input-' + this.size;
38001          }
38002          
38003          if (this.disabled) {
38004              input.disabled=true;
38005          }
38006          
38007          var inputblock = input;
38008          
38009          if(this.hasFeedback && !this.allowBlank){
38010              var feedback = {
38011                  tag: 'span',
38012                  cls: 'glyphicon form-control-feedback'
38013              };
38014          }
38015          
38016          inputblock = {
38017              cn :  []
38018          };
38019          
38020          inputblock.cn.push(input);
38021          
38022          if(this.hasFeedback && !this.allowBlank){
38023              inputblock.cls += 'has-feedback';
38024              inputblock.cn.push(feedback);
38025          }
38026          
38027          var box = {
38028              tag: 'div',
38029              cn: [
38030                  {
38031                      tag: 'input',
38032                      type : 'hidden',
38033                      cls: 'form-hidden-field'
38034                  },
38035                  inputblock
38036              ]
38037          };
38038          
38039          var flag = {
38040              tag: 'span',
38041              html: 'flag',
38042              style: 'margin-right:5px',
38043              cls: 'roo-selected-region',
38044              cn: [] //flag position ... (iti-flag-us)
38045          };
38046          
38047          var caret = {
38048              tag: 'span',
38049              cls: 'caret'
38050           };
38051           
38052          if (this.caret != false) {
38053              caret = {
38054                   tag: 'i',
38055                   cls: 'fa fa-' + this.caret
38056              };
38057          }
38058          
38059          var combobox = {
38060              cls: 'roo-select2-container input-group',
38061              cn: []
38062          };
38063          
38064          combobox.cn.push({
38065              tag :'span',
38066              cls : 'input-group-addon btn dropdown-toggle',
38067              style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38068              cn : [
38069                  flag,
38070                  caret,
38071                  {
38072                      tag: 'span',
38073                      cls: 'combobox-clear',
38074                      cn  : [
38075                          {
38076                              tag : 'i',
38077                              cls: 'icon-remove'
38078                          }
38079                      ]
38080                  }
38081              ]
38082          });
38083          
38084          combobox.cn.push(box);
38085          
38086          if (align ==='left' && this.fieldLabel.length) {
38087              
38088              cfg.cls += ' roo-form-group-label-left';
38089
38090              cfg.cn = [
38091                  {
38092                      tag : 'i',
38093                      cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38094                      tooltip : 'This field is required'
38095                  },
38096                  {
38097                      tag: 'label',
38098                      'for' :  id,
38099                      cls : 'control-label',
38100                      html : this.fieldLabel
38101
38102                  },
38103                  {
38104                      cls : "", 
38105                      cn: [
38106                          combobox
38107                      ]
38108                  }
38109              ];
38110              
38111              var labelCfg = cfg.cn[1];
38112              var contentCfg = cfg.cn[2];
38113              
38114              if(this.indicatorpos == 'right'){
38115                  cfg.cn = [
38116                      {
38117                          tag: 'label',
38118                          'for' :  id,
38119                          cls : 'control-label',
38120                          cn : [
38121                              {
38122                                  tag : 'span',
38123                                  html : this.fieldLabel
38124                              },
38125                              {
38126                                  tag : 'i',
38127                                  cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38128                                  tooltip : 'This field is required'
38129                              }
38130                          ]
38131                      },
38132                      {
38133                          cls : "", 
38134                          cn: [
38135                              combobox
38136                          ]
38137                      }
38138
38139                  ];
38140                  
38141                  labelCfg = cfg.cn[0];
38142                  contentCfg = cfg.cn[1];
38143              }
38144              
38145              if(this.labelWidth > 12){
38146                  labelCfg.style = "width: " + this.labelWidth + 'px';
38147              }
38148              
38149              if(this.labelWidth < 13 && this.labelmd == 0){
38150                  this.labelmd = this.labelWidth;
38151              }
38152              
38153              if(this.labellg > 0){
38154                  labelCfg.cls += ' col-lg-' + this.labellg;
38155                  contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38156              }
38157              
38158              if(this.labelmd > 0){
38159                  labelCfg.cls += ' col-md-' + this.labelmd;
38160                  contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38161              }
38162              
38163              if(this.labelsm > 0){
38164                  labelCfg.cls += ' col-sm-' + this.labelsm;
38165                  contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38166              }
38167              
38168              if(this.labelxs > 0){
38169                  labelCfg.cls += ' col-xs-' + this.labelxs;
38170                  contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38171              }
38172              
38173          } else if ( this.fieldLabel.length) {
38174  //                Roo.log(" label");
38175              cfg.cn = [
38176                  {
38177                     tag : 'i',
38178                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38179                     tooltip : 'This field is required'
38180                 },
38181                 {
38182                     tag: 'label',
38183                     //cls : 'input-group-addon',
38184                     html : this.fieldLabel
38185
38186                 },
38187
38188                 combobox
38189
38190              ];
38191              
38192              if(this.indicatorpos == 'right'){
38193                  
38194                  cfg.cn = [
38195                      {
38196                         tag: 'label',
38197                         cn : [
38198                             {
38199                                 tag : 'span',
38200                                 html : this.fieldLabel
38201                             },
38202                             {
38203                                tag : 'i',
38204                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38205                                tooltip : 'This field is required'
38206                             }
38207                         ]
38208                      },
38209                      combobox
38210                  ];
38211              }
38212          } else {
38213                  cfg = combobox
38214          }
38215          
38216          var settings=this;
38217          ['xs','sm','md','lg'].map(function(size){
38218              if (settings[size]) {
38219                  cfg.cls += ' col-' + size + '-' + settings[size];
38220              }
38221          });
38222          
38223          return cfg;
38224      },
38225      
38226      _initEventsCalled : false,
38227      
38228      initEvents: function()
38229      {   
38230          if (this._initEventsCalled) {
38231              return;
38232          }
38233          
38234          this._initEventsCalled = true;
38235          
38236          this.store =  new Roo.data.SimpleStore({
38237              data : this.list,
38238              fields : ['name','iso','dial_code','order','area_code']
38239          });
38240          
38241          this.store = Roo.factory(this.store, Roo.data);
38242          this.store.parent = this;
38243          
38244          Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38245          
38246          var _this = this;
38247          
38248          (function(){
38249              var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38250              _this.list.setWidth(lw);
38251          }).defer(100);
38252          
38253          this.list.on('mouseover', this.onViewOver, this);
38254          this.list.on('mousemove', this.onViewMove, this);
38255          this.list.on('scroll', this.onViewScroll, this);
38256          
38257          if(!this.tpl){
38258              this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38259          }
38260
38261          this.view = new Roo.View(this.list, this.tpl, {
38262              singleSelect:true, store: this.store, selectedClass: this.selectedClass
38263          });
38264          
38265          this.view.on('click', this.onViewClick, this);
38266          
38267          this.store.on('beforeload', this.onBeforeLoad, this);
38268          this.store.on('load', this.onLoad, this);
38269          this.store.on('loadexception', this.onLoadException, this);
38270          
38271          this.keyNav = new Roo.KeyNav(this.inputEl(), {
38272              "up" : function(e){
38273                  this.inKeyMode = true;
38274                  this.selectPrev();
38275              },
38276
38277              "down" : function(e){
38278                  if(!this.isExpanded()){
38279                      this.onTriggerClick();
38280                  }else{
38281                      this.inKeyMode = true;
38282                      this.selectNext();
38283                  }
38284              },
38285
38286              "enter" : function(e){
38287  //                this.onViewClick();
38288                  //return true;
38289                  this.collapse();
38290                  
38291                  if(this.fireEvent("specialkey", this, e)){
38292                      this.onViewClick(false);
38293                  }
38294                  
38295                  return true;
38296              },
38297
38298              "esc" : function(e){
38299                  this.collapse();
38300              },
38301
38302              "tab" : function(e){
38303                  this.collapse();
38304                  
38305                  if(this.fireEvent("specialkey", this, e)){
38306                      this.onViewClick(false);
38307                  }
38308                  
38309                  return true;
38310              },
38311
38312              scope : this,
38313
38314              doRelay : function(foo, bar, hname){
38315                  if(hname == 'down' || this.scope.isExpanded()){
38316                     return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38317                  }
38318                  return true;
38319              },
38320
38321              forceKeyDown: true
38322          });
38323          
38324     },
38325     
38326     onViewOver : function(e, t){
38327         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38328             return;
38329         }
38330         var item = this.view.findItemFromChild(t);
38331         
38332         if(item){
38333             var index = this.view.indexOf(item);
38334             this.select(index, false);
38335         }
38336     },
38337     
38338     onViewMove : function(e, t){
38339         this.inKeyMode = false;
38340     },
38341     
38342     onViewScroll : function(e, t){
38343         
38344         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){
38345             return;
38346         }
38347         
38348         this.hasQuery = true;
38349         
38350         this.loading = this.list.select('.loading', true).first();
38351         
38352         if(this.loading === null){
38353             this.list.createChild({
38354                 tag: 'div',
38355                 cls: 'loading roo-select2-more-results roo-select2-active',
38356                 html: 'Loading more results...'
38357             });
38358             
38359             this.loading = this.list.select('.loading', true).first();
38360             
38361             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38362             
38363             this.loading.hide();
38364         }
38365         
38366         this.loading.show();
38367         
38368         var _combo = this;
38369         
38370         this.page++;
38371         this.loadNext = true;
38372         
38373         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38374         
38375         return;
38376     },
38377     
38378     onTriggerClick : function(e)
38379     {
38380         Roo.log('trigger click');
38381         
38382         if(this.disabled || !this.triggerList){
38383             return;
38384         }
38385         
38386         this.page = 0;
38387         this.loadNext = false;
38388         
38389         if(this.isExpanded()){
38390             this.collapse();
38391             if (!this.blockFocus) {
38392                 this.inputEl().focus();
38393             }
38394             
38395         }else {
38396             this.hasFocus = true;
38397             if(this.triggerAction == 'all') {
38398                 this.doQuery(this.allQuery, true);
38399             } else {
38400                 this.doQuery(this.getRawValue());
38401             }
38402             if (!this.blockFocus) {
38403                 this.inputEl().focus();
38404             }
38405         }
38406     }
38407     
38408  });
38409
38410  Roo.apply(Roo.bootstrap.PhoneInput, {
38411      
38412      /**
38413       * iso2 and abbr for all countries
38414       * @type Object
38415       */
38416      List : [
38417          ["Afghanistan (‫افغانستان‬‎)", "af", "93"],
38418          ["Albania (Shqipëri)", "al", "355"],
38419          ["Algeria (‫الجزائر‬‎)", "dz", "213"],
38420          ["American Samoa", "as", "1684"],
38421          ["Andorra", "ad", "376"],
38422          ["Angola", "ao", "244"],
38423          ["Anguilla", "ai", "1264"],
38424          ["Antigua and Barbuda", "ag", "1268"],
38425          ["Argentina", "ar", "54"],
38426          ["Armenia (Հայաստան)", "am", "374"],
38427          ["Aruba", "aw", "297"],
38428          ["Australia", "au", "61", 0],
38429          ["Austria (Österreich)", "at", "43"],
38430          ["Azerbaijan (Azərbaycan)", "az", "994"],
38431          ["Bahamas", "bs", "1242"],
38432          ["Bahrain (‫البحرين‬‎)", "bh", "973"],
38433          ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38434          ["Barbados", "bb", "1246"],
38435          ["Belarus (Беларусь)", "by", "375"],
38436          ["Belgium (België)", "be", "32"],
38437          ["Belize", "bz", "501"],
38438          ["Benin (Bénin)", "bj", "229"],
38439          ["Bermuda", "bm", "1441"],
38440          ["Bhutan (འབྲུག)", "bt", "975"],
38441          ["Bolivia", "bo", "591"],
38442          ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38443          ["Botswana", "bw", "267"],
38444          ["Brazil (Brasil)", "br", "55"],
38445          ["British Indian Ocean Territory", "io", "246"],
38446          ["British Virgin Islands", "vg", "1284"],
38447          ["Brunei", "bn", "673"],
38448          ["Bulgaria (България)", "bg", "359"],
38449          ["Burkina Faso", "bf", "226"],
38450          ["Burundi (Uburundi)", "bi", "257"],
38451          ["Cambodia (កម្ពុជា)", "kh", "855"],
38452          ["Cameroon (Cameroun)", "cm", "237"],
38453          ["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"]],
38454          ["Cape Verde (Kabu Verdi)", "cv", "238"],
38455          ["Caribbean Netherlands", "bq", "599", 1],
38456          ["Cayman Islands", "ky", "1345"],
38457          ["Central African Republic (République centrafricaine)", "cf", "236"],
38458          ["Chad (Tchad)", "td", "235"],
38459          ["Chile", "cl", "56"],
38460          ["China (中国)", "cn", "86"],
38461          ["Christmas Island", "cx", "61", 2],
38462          ["Cocos (Keeling) Islands", "cc", "61", 1],
38463          ["Colombia", "co", "57"],
38464          ["Comoros (‫جزر القمر‬‎)", "km", "269"],
38465          ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38466          ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38467          ["Cook Islands", "ck", "682"],
38468          ["Costa Rica", "cr", "506"],
38469          ["Côte d’Ivoire", "ci", "225"],
38470          ["Croatia (Hrvatska)", "hr", "385"],
38471          ["Cuba", "cu", "53"],
38472          ["Curaçao", "cw", "599", 0],
38473          ["Cyprus (Κύπρος)", "cy", "357"],
38474          ["Czech Republic (Česká republika)", "cz", "420"],
38475          ["Denmark (Danmark)", "dk", "45"],
38476          ["Djibouti", "dj", "253"],
38477          ["Dominica", "dm", "1767"],
38478          ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38479          ["Ecuador", "ec", "593"],
38480          ["Egypt (‫مصر‬‎)", "eg", "20"],
38481          ["El Salvador", "sv", "503"],
38482          ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38483          ["Eritrea", "er", "291"],
38484          ["Estonia (Eesti)", "ee", "372"],
38485          ["Ethiopia", "et", "251"],
38486          ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38487          ["Faroe Islands (Føroyar)", "fo", "298"],
38488          ["Fiji", "fj", "679"],
38489          ["Finland (Suomi)", "fi", "358", 0],
38490          ["France", "fr", "33"],
38491          ["French Guiana (Guyane française)", "gf", "594"],
38492          ["French Polynesia (Polynésie française)", "pf", "689"],
38493          ["Gabon", "ga", "241"],
38494          ["Gambia", "gm", "220"],
38495          ["Georgia (საქართველო)", "ge", "995"],
38496          ["Germany (Deutschland)", "de", "49"],
38497          ["Ghana (Gaana)", "gh", "233"],
38498          ["Gibraltar", "gi", "350"],
38499          ["Greece (Ελλάδα)", "gr", "30"],
38500          ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38501          ["Grenada", "gd", "1473"],
38502          ["Guadeloupe", "gp", "590", 0],
38503          ["Guam", "gu", "1671"],
38504          ["Guatemala", "gt", "502"],
38505          ["Guernsey", "gg", "44", 1],
38506          ["Guinea (Guinée)", "gn", "224"],
38507          ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38508          ["Guyana", "gy", "592"],
38509          ["Haiti", "ht", "509"],
38510          ["Honduras", "hn", "504"],
38511          ["Hong Kong (香港)", "hk", "852"],
38512          ["Hungary (Magyarország)", "hu", "36"],
38513          ["Iceland (Ísland)", "is", "354"],
38514          ["India (भारत)", "in", "91"],
38515          ["Indonesia", "id", "62"],
38516          ["Iran (‫ایران‬‎)", "ir", "98"],
38517          ["Iraq (‫العراق‬‎)", "iq", "964"],
38518          ["Ireland", "ie", "353"],
38519          ["Isle of Man", "im", "44", 2],
38520          ["Israel (‫ישראל‬‎)", "il", "972"],
38521          ["Italy (Italia)", "it", "39", 0],
38522          ["Jamaica", "jm", "1876"],
38523          ["Japan (日本)", "jp", "81"],
38524          ["Jersey", "je", "44", 3],
38525          ["Jordan (‫الأردن‬‎)", "jo", "962"],
38526          ["Kazakhstan (Казахстан)", "kz", "7", 1],
38527          ["Kenya", "ke", "254"],
38528          ["Kiribati", "ki", "686"],
38529          ["Kosovo", "xk", "383"],
38530          ["Kuwait (‫الكويت‬‎)", "kw", "965"],
38531          ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38532          ["Laos (ລາວ)", "la", "856"],
38533          ["Latvia (Latvija)", "lv", "371"],
38534          ["Lebanon (‫لبنان‬‎)", "lb", "961"],
38535          ["Lesotho", "ls", "266"],
38536          ["Liberia", "lr", "231"],
38537          ["Libya (‫ليبيا‬‎)", "ly", "218"],
38538          ["Liechtenstein", "li", "423"],
38539          ["Lithuania (Lietuva)", "lt", "370"],
38540          ["Luxembourg", "lu", "352"],
38541          ["Macau (澳門)", "mo", "853"],
38542          ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38543          ["Madagascar (Madagasikara)", "mg", "261"],
38544          ["Malawi", "mw", "265"],
38545          ["Malaysia", "my", "60"],
38546          ["Maldives", "mv", "960"],
38547          ["Mali", "ml", "223"],
38548          ["Malta", "mt", "356"],
38549          ["Marshall Islands", "mh", "692"],
38550          ["Martinique", "mq", "596"],
38551          ["Mauritania (‫موريتانيا‬‎)", "mr", "222"],
38552          ["Mauritius (Moris)", "mu", "230"],
38553          ["Mayotte", "yt", "262", 1],
38554          ["Mexico (México)", "mx", "52"],
38555          ["Micronesia", "fm", "691"],
38556          ["Moldova (Republica Moldova)", "md", "373"],
38557          ["Monaco", "mc", "377"],
38558          ["Mongolia (Монгол)", "mn", "976"],
38559          ["Montenegro (Crna Gora)", "me", "382"],
38560          ["Montserrat", "ms", "1664"],
38561          ["Morocco (‫المغرب‬‎)", "ma", "212", 0],
38562          ["Mozambique (Moçambique)", "mz", "258"],
38563          ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38564          ["Namibia (Namibië)", "na", "264"],
38565          ["Nauru", "nr", "674"],
38566          ["Nepal (नेपाल)", "np", "977"],
38567          ["Netherlands (Nederland)", "nl", "31"],
38568          ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38569          ["New Zealand", "nz", "64"],
38570          ["Nicaragua", "ni", "505"],
38571          ["Niger (Nijar)", "ne", "227"],
38572          ["Nigeria", "ng", "234"],
38573          ["Niue", "nu", "683"],
38574          ["Norfolk Island", "nf", "672"],
38575          ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38576          ["Northern Mariana Islands", "mp", "1670"],
38577          ["Norway (Norge)", "no", "47", 0],
38578          ["Oman (‫عُمان‬‎)", "om", "968"],
38579          ["Pakistan (‫پاکستان‬‎)", "pk", "92"],
38580          ["Palau", "pw", "680"],
38581          ["Palestine (‫فلسطين‬‎)", "ps", "970"],
38582          ["Panama (Panamá)", "pa", "507"],
38583          ["Papua New Guinea", "pg", "675"],
38584          ["Paraguay", "py", "595"],
38585          ["Peru (Perú)", "pe", "51"],
38586          ["Philippines", "ph", "63"],
38587          ["Poland (Polska)", "pl", "48"],
38588          ["Portugal", "pt", "351"],
38589          ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38590          ["Qatar (‫قطر‬‎)", "qa", "974"],
38591          ["Réunion (La Réunion)", "re", "262", 0],
38592          ["Romania (România)", "ro", "40"],
38593          ["Russia (Россия)", "ru", "7", 0],
38594          ["Rwanda", "rw", "250"],
38595          ["Saint Barthélemy", "bl", "590", 1],
38596          ["Saint Helena", "sh", "290"],
38597          ["Saint Kitts and Nevis", "kn", "1869"],
38598          ["Saint Lucia", "lc", "1758"],
38599          ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38600          ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38601          ["Saint Vincent and the Grenadines", "vc", "1784"],
38602          ["Samoa", "ws", "685"],
38603          ["San Marino", "sm", "378"],
38604          ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38605          ["Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966"],
38606          ["Senegal (Sénégal)", "sn", "221"],
38607          ["Serbia (Србија)", "rs", "381"],
38608          ["Seychelles", "sc", "248"],
38609          ["Sierra Leone", "sl", "232"],
38610          ["Singapore", "sg", "65"],
38611          ["Sint Maarten", "sx", "1721"],
38612          ["Slovakia (Slovensko)", "sk", "421"],
38613          ["Slovenia (Slovenija)", "si", "386"],
38614          ["Solomon Islands", "sb", "677"],
38615          ["Somalia (Soomaaliya)", "so", "252"],
38616          ["South Africa", "za", "27"],
38617          ["South Korea (대한민국)", "kr", "82"],
38618          ["South Sudan (‫جنوب السودان‬‎)", "ss", "211"],
38619          ["Spain (España)", "es", "34"],
38620          ["Sri Lanka (ශ්‍රී ලංකාව)", "lk", "94"],
38621          ["Sudan (‫السودان‬‎)", "sd", "249"],
38622          ["Suriname", "sr", "597"],
38623          ["Svalbard and Jan Mayen", "sj", "47", 1],
38624          ["Swaziland", "sz", "268"],
38625          ["Sweden (Sverige)", "se", "46"],
38626          ["Switzerland (Schweiz)", "ch", "41"],
38627          ["Syria (‫سوريا‬‎)", "sy", "963"],
38628          ["Taiwan (台灣)", "tw", "886"],
38629          ["Tajikistan", "tj", "992"],
38630          ["Tanzania", "tz", "255"],
38631          ["Thailand (ไทย)", "th", "66"],
38632          ["Timor-Leste", "tl", "670"],
38633          ["Togo", "tg", "228"],
38634          ["Tokelau", "tk", "690"],
38635          ["Tonga", "to", "676"],
38636          ["Trinidad and Tobago", "tt", "1868"],
38637          ["Tunisia (‫تونس‬‎)", "tn", "216"],
38638          ["Turkey (Türkiye)", "tr", "90"],
38639          ["Turkmenistan", "tm", "993"],
38640          ["Turks and Caicos Islands", "tc", "1649"],
38641          ["Tuvalu", "tv", "688"],
38642          ["U.S. Virgin Islands", "vi", "1340"],
38643          ["Uganda", "ug", "256"],
38644          ["Ukraine (Україна)", "ua", "380"],
38645          ["United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "ae", "971"],
38646          ["United Kingdom", "gb", "44", 0],
38647          ["United States", "us", "1", 0],
38648          ["Uruguay", "uy", "598"],
38649          ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38650          ["Vanuatu", "vu", "678"],
38651          ["Vatican City (Città del Vaticano)", "va", "39", 1],
38652          ["Venezuela", "ve", "58"],
38653          ["Vietnam (Việt Nam)", "vn", "84"],
38654          ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38655          ["Western Sahara (‫الصحراء الغربية‬‎)", "eh", "212", 1],
38656          ["Yemen (‫اليمن‬‎)", "ye", "967"],
38657          ["Zambia", "zm", "260"],
38658          ["Zimbabwe", "zw", "263"],
38659          ["Åland Islands", "ax", "358", 1]
38660      ]
38661  });