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 (default|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() && 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  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             
7971             if (!f.getName()) {
7972                 return;
7973             }
7974             
7975             var v = f.getValue();
7976             
7977             if (f.inputType =='radio') {
7978                 if (typeof(ret[f.getName()]) == 'undefined') {
7979                     ret[f.getName()] = ''; // empty..
7980                 }
7981
7982                 if (!f.el.dom.checked) {
7983                     return;
7984
7985                 }
7986                 v = f.el.dom.value;
7987
7988             }
7989             
7990             if(f.xtype == 'MoneyField'){
7991                 ret[f.currencyName] = f.getCurrency();
7992             }
7993
7994             // not sure if this supported any more..
7995             if ((typeof(v) == 'object') && f.getRawValue) {
7996                 v = f.getRawValue() ; // dates..
7997             }
7998             // combo boxes where name != hiddenName...
7999             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8000                 ret[f.name] = f.getRawValue();
8001             }
8002             ret[f.getName()] = v;
8003         });
8004
8005         return ret;
8006     },
8007
8008     /**
8009      * Clears all invalid messages in this form.
8010      * @return {BasicForm} this
8011      */
8012     clearInvalid : function(){
8013         var items = this.getItems();
8014
8015         items.each(function(f){
8016            f.clearInvalid();
8017         });
8018
8019
8020
8021         return this;
8022     },
8023
8024     /**
8025      * Resets this form.
8026      * @return {BasicForm} this
8027      */
8028     reset : function(){
8029         var items = this.getItems();
8030         items.each(function(f){
8031             f.reset();
8032         });
8033
8034         Roo.each(this.childForms || [], function (f) {
8035             f.reset();
8036         });
8037
8038
8039         return this;
8040     },
8041     
8042     getItems : function()
8043     {
8044         var r=new Roo.util.MixedCollection(false, function(o){
8045             return o.id || (o.id = Roo.id());
8046         });
8047         var iter = function(el) {
8048             if (el.inputEl) {
8049                 r.add(el);
8050             }
8051             if (!el.items) {
8052                 return;
8053             }
8054             Roo.each(el.items,function(e) {
8055                 iter(e);
8056             });
8057
8058
8059         };
8060
8061         iter(this);
8062         return r;
8063         
8064     }
8065
8066 });
8067
8068 Roo.apply(Roo.bootstrap.Form, {
8069     
8070     popover : {
8071         
8072         padding : 5,
8073         
8074         isApplied : false,
8075         
8076         isMasked : false,
8077         
8078         form : false,
8079         
8080         target : false,
8081         
8082         toolTip : false,
8083         
8084         intervalID : false,
8085         
8086         maskEl : false,
8087         
8088         apply : function()
8089         {
8090             if(this.isApplied){
8091                 return;
8092             }
8093             
8094             this.maskEl = {
8095                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8096                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8097                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8098                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8099             };
8100             
8101             this.maskEl.top.enableDisplayMode("block");
8102             this.maskEl.left.enableDisplayMode("block");
8103             this.maskEl.bottom.enableDisplayMode("block");
8104             this.maskEl.right.enableDisplayMode("block");
8105             
8106             this.toolTip = new Roo.bootstrap.Tooltip({
8107                 cls : 'roo-form-error-popover',
8108                 alignment : {
8109                     'left' : ['r-l', [-2,0], 'right'],
8110                     'right' : ['l-r', [2,0], 'left'],
8111                     'bottom' : ['tl-bl', [0,2], 'top'],
8112                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8113                 }
8114             });
8115             
8116             this.toolTip.render(Roo.get(document.body));
8117
8118             this.toolTip.el.enableDisplayMode("block");
8119             
8120             Roo.get(document.body).on('click', function(){
8121                 this.unmask();
8122             }, this);
8123             
8124             Roo.get(document.body).on('touchstart', function(){
8125                 this.unmask();
8126             }, this);
8127             
8128             this.isApplied = true
8129         },
8130         
8131         mask : function(form, target)
8132         {
8133             this.form = form;
8134             
8135             this.target = target;
8136             
8137             if(!this.form.errorMask || !target.el){
8138                 return;
8139             }
8140             
8141             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8142             
8143             Roo.log(scrollable);
8144             
8145             var ot = this.target.el.calcOffsetsTo(scrollable);
8146             
8147             var scrollTo = ot[1] - this.form.maskOffset;
8148             
8149             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8150             
8151             scrollable.scrollTo('top', scrollTo);
8152             
8153             var box = this.target.el.getBox();
8154             Roo.log(box);
8155             var zIndex = Roo.bootstrap.Modal.zIndex++;
8156
8157             
8158             this.maskEl.top.setStyle('position', 'absolute');
8159             this.maskEl.top.setStyle('z-index', zIndex);
8160             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8161             this.maskEl.top.setLeft(0);
8162             this.maskEl.top.setTop(0);
8163             this.maskEl.top.show();
8164             
8165             this.maskEl.left.setStyle('position', 'absolute');
8166             this.maskEl.left.setStyle('z-index', zIndex);
8167             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8168             this.maskEl.left.setLeft(0);
8169             this.maskEl.left.setTop(box.y - this.padding);
8170             this.maskEl.left.show();
8171
8172             this.maskEl.bottom.setStyle('position', 'absolute');
8173             this.maskEl.bottom.setStyle('z-index', zIndex);
8174             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8175             this.maskEl.bottom.setLeft(0);
8176             this.maskEl.bottom.setTop(box.bottom + this.padding);
8177             this.maskEl.bottom.show();
8178
8179             this.maskEl.right.setStyle('position', 'absolute');
8180             this.maskEl.right.setStyle('z-index', zIndex);
8181             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8182             this.maskEl.right.setLeft(box.right + this.padding);
8183             this.maskEl.right.setTop(box.y - this.padding);
8184             this.maskEl.right.show();
8185
8186             this.toolTip.bindEl = this.target.el;
8187
8188             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8189
8190             var tip = this.target.blankText;
8191
8192             if(this.target.getValue() !== '' ) {
8193                 
8194                 if (this.target.invalidText.length) {
8195                     tip = this.target.invalidText;
8196                 } else if (this.target.regexText.length){
8197                     tip = this.target.regexText;
8198                 }
8199             }
8200
8201             this.toolTip.show(tip);
8202
8203             this.intervalID = window.setInterval(function() {
8204                 Roo.bootstrap.Form.popover.unmask();
8205             }, 10000);
8206
8207             window.onwheel = function(){ return false;};
8208             
8209             (function(){ this.isMasked = true; }).defer(500, this);
8210             
8211         },
8212         
8213         unmask : function()
8214         {
8215             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8216                 return;
8217             }
8218             
8219             this.maskEl.top.setStyle('position', 'absolute');
8220             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8221             this.maskEl.top.hide();
8222
8223             this.maskEl.left.setStyle('position', 'absolute');
8224             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8225             this.maskEl.left.hide();
8226
8227             this.maskEl.bottom.setStyle('position', 'absolute');
8228             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8229             this.maskEl.bottom.hide();
8230
8231             this.maskEl.right.setStyle('position', 'absolute');
8232             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8233             this.maskEl.right.hide();
8234             
8235             this.toolTip.hide();
8236             
8237             this.toolTip.el.hide();
8238             
8239             window.onwheel = function(){ return true;};
8240             
8241             if(this.intervalID){
8242                 window.clearInterval(this.intervalID);
8243                 this.intervalID = false;
8244             }
8245             
8246             this.isMasked = false;
8247             
8248         }
8249         
8250     }
8251     
8252 });
8253
8254 /*
8255  * Based on:
8256  * Ext JS Library 1.1.1
8257  * Copyright(c) 2006-2007, Ext JS, LLC.
8258  *
8259  * Originally Released Under LGPL - original licence link has changed is not relivant.
8260  *
8261  * Fork - LGPL
8262  * <script type="text/javascript">
8263  */
8264 /**
8265  * @class Roo.form.VTypes
8266  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8267  * @singleton
8268  */
8269 Roo.form.VTypes = function(){
8270     // closure these in so they are only created once.
8271     var alpha = /^[a-zA-Z_]+$/;
8272     var alphanum = /^[a-zA-Z0-9_]+$/;
8273     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8274     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8275
8276     // All these messages and functions are configurable
8277     return {
8278         /**
8279          * The function used to validate email addresses
8280          * @param {String} value The email address
8281          */
8282         'email' : function(v){
8283             return email.test(v);
8284         },
8285         /**
8286          * The error text to display when the email validation function returns false
8287          * @type String
8288          */
8289         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8290         /**
8291          * The keystroke filter mask to be applied on email input
8292          * @type RegExp
8293          */
8294         'emailMask' : /[a-z0-9_\.\-@]/i,
8295
8296         /**
8297          * The function used to validate URLs
8298          * @param {String} value The URL
8299          */
8300         'url' : function(v){
8301             return url.test(v);
8302         },
8303         /**
8304          * The error text to display when the url validation function returns false
8305          * @type String
8306          */
8307         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8308         
8309         /**
8310          * The function used to validate alpha values
8311          * @param {String} value The value
8312          */
8313         'alpha' : function(v){
8314             return alpha.test(v);
8315         },
8316         /**
8317          * The error text to display when the alpha validation function returns false
8318          * @type String
8319          */
8320         'alphaText' : 'This field should only contain letters and _',
8321         /**
8322          * The keystroke filter mask to be applied on alpha input
8323          * @type RegExp
8324          */
8325         'alphaMask' : /[a-z_]/i,
8326
8327         /**
8328          * The function used to validate alphanumeric values
8329          * @param {String} value The value
8330          */
8331         'alphanum' : function(v){
8332             return alphanum.test(v);
8333         },
8334         /**
8335          * The error text to display when the alphanumeric validation function returns false
8336          * @type String
8337          */
8338         'alphanumText' : 'This field should only contain letters, numbers and _',
8339         /**
8340          * The keystroke filter mask to be applied on alphanumeric input
8341          * @type RegExp
8342          */
8343         'alphanumMask' : /[a-z0-9_]/i
8344     };
8345 }();/*
8346  * - LGPL
8347  *
8348  * Input
8349  * 
8350  */
8351
8352 /**
8353  * @class Roo.bootstrap.Input
8354  * @extends Roo.bootstrap.Component
8355  * Bootstrap Input class
8356  * @cfg {Boolean} disabled is it disabled
8357  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8358  * @cfg {String} name name of the input
8359  * @cfg {string} fieldLabel - the label associated
8360  * @cfg {string} placeholder - placeholder to put in text.
8361  * @cfg {string}  before - input group add on before
8362  * @cfg {string} after - input group add on after
8363  * @cfg {string} size - (lg|sm) or leave empty..
8364  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8365  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8366  * @cfg {Number} md colspan out of 12 for computer-sized screens
8367  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8368  * @cfg {string} value default value of the input
8369  * @cfg {Number} labelWidth set the width of label 
8370  * @cfg {Number} labellg set the width of label (1-12)
8371  * @cfg {Number} labelmd set the width of label (1-12)
8372  * @cfg {Number} labelsm set the width of label (1-12)
8373  * @cfg {Number} labelxs set the width of label (1-12)
8374  * @cfg {String} labelAlign (top|left)
8375  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8376  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8377  * @cfg {String} indicatorpos (left|right) default left
8378
8379  * @cfg {String} align (left|center|right) Default left
8380  * @cfg {Boolean} forceFeedback (true|false) Default false
8381  * 
8382  * 
8383  * 
8384  * 
8385  * @constructor
8386  * Create a new Input
8387  * @param {Object} config The config object
8388  */
8389
8390 Roo.bootstrap.Input = function(config){
8391     
8392     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8393     
8394     this.addEvents({
8395         /**
8396          * @event focus
8397          * Fires when this field receives input focus.
8398          * @param {Roo.form.Field} this
8399          */
8400         focus : true,
8401         /**
8402          * @event blur
8403          * Fires when this field loses input focus.
8404          * @param {Roo.form.Field} this
8405          */
8406         blur : true,
8407         /**
8408          * @event specialkey
8409          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8410          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8411          * @param {Roo.form.Field} this
8412          * @param {Roo.EventObject} e The event object
8413          */
8414         specialkey : true,
8415         /**
8416          * @event change
8417          * Fires just before the field blurs if the field value has changed.
8418          * @param {Roo.form.Field} this
8419          * @param {Mixed} newValue The new value
8420          * @param {Mixed} oldValue The original value
8421          */
8422         change : true,
8423         /**
8424          * @event invalid
8425          * Fires after the field has been marked as invalid.
8426          * @param {Roo.form.Field} this
8427          * @param {String} msg The validation message
8428          */
8429         invalid : true,
8430         /**
8431          * @event valid
8432          * Fires after the field has been validated with no errors.
8433          * @param {Roo.form.Field} this
8434          */
8435         valid : true,
8436          /**
8437          * @event keyup
8438          * Fires after the key up
8439          * @param {Roo.form.Field} this
8440          * @param {Roo.EventObject}  e The event Object
8441          */
8442         keyup : true
8443     });
8444 };
8445
8446 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8447      /**
8448      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8449       automatic validation (defaults to "keyup").
8450      */
8451     validationEvent : "keyup",
8452      /**
8453      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8454      */
8455     validateOnBlur : true,
8456     /**
8457      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8458      */
8459     validationDelay : 250,
8460      /**
8461      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8462      */
8463     focusClass : "x-form-focus",  // not needed???
8464     
8465        
8466     /**
8467      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8468      */
8469     invalidClass : "has-warning",
8470     
8471     /**
8472      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8473      */
8474     validClass : "has-success",
8475     
8476     /**
8477      * @cfg {Boolean} hasFeedback (true|false) default true
8478      */
8479     hasFeedback : true,
8480     
8481     /**
8482      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8483      */
8484     invalidFeedbackClass : "glyphicon-warning-sign",
8485     
8486     /**
8487      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8488      */
8489     validFeedbackClass : "glyphicon-ok",
8490     
8491     /**
8492      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8493      */
8494     selectOnFocus : false,
8495     
8496      /**
8497      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8498      */
8499     maskRe : null,
8500        /**
8501      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8502      */
8503     vtype : null,
8504     
8505       /**
8506      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8507      */
8508     disableKeyFilter : false,
8509     
8510        /**
8511      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8512      */
8513     disabled : false,
8514      /**
8515      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8516      */
8517     allowBlank : true,
8518     /**
8519      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8520      */
8521     blankText : "Please complete this mandatory field",
8522     
8523      /**
8524      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8525      */
8526     minLength : 0,
8527     /**
8528      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8529      */
8530     maxLength : Number.MAX_VALUE,
8531     /**
8532      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8533      */
8534     minLengthText : "The minimum length for this field is {0}",
8535     /**
8536      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8537      */
8538     maxLengthText : "The maximum length for this field is {0}",
8539   
8540     
8541     /**
8542      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8543      * If available, this function will be called only after the basic validators all return true, and will be passed the
8544      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8545      */
8546     validator : null,
8547     /**
8548      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8549      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8550      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8551      */
8552     regex : null,
8553     /**
8554      * @cfg {String} regexText -- Depricated - use Invalid Text
8555      */
8556     regexText : "",
8557     
8558     /**
8559      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8560      */
8561     invalidText : "",
8562     
8563     
8564     
8565     autocomplete: false,
8566     
8567     
8568     fieldLabel : '',
8569     inputType : 'text',
8570     
8571     name : false,
8572     placeholder: false,
8573     before : false,
8574     after : false,
8575     size : false,
8576     hasFocus : false,
8577     preventMark: false,
8578     isFormField : true,
8579     value : '',
8580     labelWidth : 2,
8581     labelAlign : false,
8582     readOnly : false,
8583     align : false,
8584     formatedValue : false,
8585     forceFeedback : false,
8586     
8587     indicatorpos : 'left',
8588     
8589     labellg : 0,
8590     labelmd : 0,
8591     labelsm : 0,
8592     labelxs : 0,
8593     
8594     parentLabelAlign : function()
8595     {
8596         var parent = this;
8597         while (parent.parent()) {
8598             parent = parent.parent();
8599             if (typeof(parent.labelAlign) !='undefined') {
8600                 return parent.labelAlign;
8601             }
8602         }
8603         return 'left';
8604         
8605     },
8606     
8607     getAutoCreate : function()
8608     {
8609         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8610         
8611         var id = Roo.id();
8612         
8613         var cfg = {};
8614         
8615         if(this.inputType != 'hidden'){
8616             cfg.cls = 'form-group' //input-group
8617         }
8618         
8619         var input =  {
8620             tag: 'input',
8621             id : id,
8622             type : this.inputType,
8623             value : this.value,
8624             cls : 'form-control',
8625             placeholder : this.placeholder || '',
8626             autocomplete : this.autocomplete || 'new-password'
8627         };
8628         
8629         if(this.align){
8630             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8631         }
8632         
8633         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8634             input.maxLength = this.maxLength;
8635         }
8636         
8637         if (this.disabled) {
8638             input.disabled=true;
8639         }
8640         
8641         if (this.readOnly) {
8642             input.readonly=true;
8643         }
8644         
8645         if (this.name) {
8646             input.name = this.name;
8647         }
8648         
8649         if (this.size) {
8650             input.cls += ' input-' + this.size;
8651         }
8652         
8653         var settings=this;
8654         ['xs','sm','md','lg'].map(function(size){
8655             if (settings[size]) {
8656                 cfg.cls += ' col-' + size + '-' + settings[size];
8657             }
8658         });
8659         
8660         var inputblock = input;
8661         
8662         var feedback = {
8663             tag: 'span',
8664             cls: 'glyphicon form-control-feedback'
8665         };
8666             
8667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8668             
8669             inputblock = {
8670                 cls : 'has-feedback',
8671                 cn :  [
8672                     input,
8673                     feedback
8674                 ] 
8675             };  
8676         }
8677         
8678         if (this.before || this.after) {
8679             
8680             inputblock = {
8681                 cls : 'input-group',
8682                 cn :  [] 
8683             };
8684             
8685             if (this.before && typeof(this.before) == 'string') {
8686                 
8687                 inputblock.cn.push({
8688                     tag :'span',
8689                     cls : 'roo-input-before input-group-addon',
8690                     html : this.before
8691                 });
8692             }
8693             if (this.before && typeof(this.before) == 'object') {
8694                 this.before = Roo.factory(this.before);
8695                 
8696                 inputblock.cn.push({
8697                     tag :'span',
8698                     cls : 'roo-input-before input-group-' +
8699                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8700                 });
8701             }
8702             
8703             inputblock.cn.push(input);
8704             
8705             if (this.after && typeof(this.after) == 'string') {
8706                 inputblock.cn.push({
8707                     tag :'span',
8708                     cls : 'roo-input-after input-group-addon',
8709                     html : this.after
8710                 });
8711             }
8712             if (this.after && typeof(this.after) == 'object') {
8713                 this.after = Roo.factory(this.after);
8714                 
8715                 inputblock.cn.push({
8716                     tag :'span',
8717                     cls : 'roo-input-after input-group-' +
8718                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8719                 });
8720             }
8721             
8722             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8723                 inputblock.cls += ' has-feedback';
8724                 inputblock.cn.push(feedback);
8725             }
8726         };
8727         
8728         if (align ==='left' && this.fieldLabel.length) {
8729             
8730             cfg.cls += ' roo-form-group-label-left';
8731             
8732             cfg.cn = [
8733                 {
8734                     tag : 'i',
8735                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8736                     tooltip : 'This field is required'
8737                 },
8738                 {
8739                     tag: 'label',
8740                     'for' :  id,
8741                     cls : 'control-label',
8742                     html : this.fieldLabel
8743
8744                 },
8745                 {
8746                     cls : "", 
8747                     cn: [
8748                         inputblock
8749                     ]
8750                 }
8751             ];
8752             
8753             var labelCfg = cfg.cn[1];
8754             var contentCfg = cfg.cn[2];
8755             
8756             if(this.indicatorpos == 'right'){
8757                 cfg.cn = [
8758                     {
8759                         tag: 'label',
8760                         'for' :  id,
8761                         cls : 'control-label',
8762                         cn : [
8763                             {
8764                                 tag : 'span',
8765                                 html : this.fieldLabel
8766                             },
8767                             {
8768                                 tag : 'i',
8769                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8770                                 tooltip : 'This field is required'
8771                             }
8772                         ]
8773                     },
8774                     {
8775                         cls : "",
8776                         cn: [
8777                             inputblock
8778                         ]
8779                     }
8780
8781                 ];
8782                 
8783                 labelCfg = cfg.cn[0];
8784                 contentCfg = cfg.cn[1];
8785             
8786             }
8787             
8788             if(this.labelWidth > 12){
8789                 labelCfg.style = "width: " + this.labelWidth + 'px';
8790             }
8791             
8792             if(this.labelWidth < 13 && this.labelmd == 0){
8793                 this.labelmd = this.labelWidth;
8794             }
8795             
8796             if(this.labellg > 0){
8797                 labelCfg.cls += ' col-lg-' + this.labellg;
8798                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8799             }
8800             
8801             if(this.labelmd > 0){
8802                 labelCfg.cls += ' col-md-' + this.labelmd;
8803                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8804             }
8805             
8806             if(this.labelsm > 0){
8807                 labelCfg.cls += ' col-sm-' + this.labelsm;
8808                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8809             }
8810             
8811             if(this.labelxs > 0){
8812                 labelCfg.cls += ' col-xs-' + this.labelxs;
8813                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8814             }
8815             
8816             
8817         } else if ( this.fieldLabel.length) {
8818                 
8819             cfg.cn = [
8820                 {
8821                     tag : 'i',
8822                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8823                     tooltip : 'This field is required'
8824                 },
8825                 {
8826                     tag: 'label',
8827                    //cls : 'input-group-addon',
8828                     html : this.fieldLabel
8829
8830                 },
8831
8832                inputblock
8833
8834            ];
8835            
8836            if(this.indicatorpos == 'right'){
8837                 
8838                 cfg.cn = [
8839                     {
8840                         tag: 'label',
8841                        //cls : 'input-group-addon',
8842                         html : this.fieldLabel
8843
8844                     },
8845                     {
8846                         tag : 'i',
8847                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8848                         tooltip : 'This field is required'
8849                     },
8850
8851                    inputblock
8852
8853                ];
8854
8855             }
8856
8857         } else {
8858             
8859             cfg.cn = [
8860
8861                     inputblock
8862
8863             ];
8864                 
8865                 
8866         };
8867         
8868         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8869            cfg.cls += ' navbar-form';
8870         }
8871         
8872         if (this.parentType === 'NavGroup') {
8873            cfg.cls += ' navbar-form';
8874            cfg.tag = 'li';
8875         }
8876         
8877         return cfg;
8878         
8879     },
8880     /**
8881      * return the real input element.
8882      */
8883     inputEl: function ()
8884     {
8885         return this.el.select('input.form-control',true).first();
8886     },
8887     
8888     tooltipEl : function()
8889     {
8890         return this.inputEl();
8891     },
8892     
8893     indicatorEl : function()
8894     {
8895         var indicator = this.el.select('i.roo-required-indicator',true).first();
8896         
8897         if(!indicator){
8898             return false;
8899         }
8900         
8901         return indicator;
8902         
8903     },
8904     
8905     setDisabled : function(v)
8906     {
8907         var i  = this.inputEl().dom;
8908         if (!v) {
8909             i.removeAttribute('disabled');
8910             return;
8911             
8912         }
8913         i.setAttribute('disabled','true');
8914     },
8915     initEvents : function()
8916     {
8917           
8918         this.inputEl().on("keydown" , this.fireKey,  this);
8919         this.inputEl().on("focus", this.onFocus,  this);
8920         this.inputEl().on("blur", this.onBlur,  this);
8921         
8922         this.inputEl().relayEvent('keyup', this);
8923         
8924         this.indicator = this.indicatorEl();
8925         
8926         if(this.indicator){
8927             this.indicator.addClass('invisible');
8928             
8929         }
8930  
8931         // reference to original value for reset
8932         this.originalValue = this.getValue();
8933         //Roo.form.TextField.superclass.initEvents.call(this);
8934         if(this.validationEvent == 'keyup'){
8935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8936             this.inputEl().on('keyup', this.filterValidation, this);
8937         }
8938         else if(this.validationEvent !== false){
8939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8940         }
8941         
8942         if(this.selectOnFocus){
8943             this.on("focus", this.preFocus, this);
8944             
8945         }
8946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8947             this.inputEl().on("keypress", this.filterKeys, this);
8948         } else {
8949             this.inputEl().relayEvent('keypress', this);
8950         }
8951        /* if(this.grow){
8952             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8953             this.el.on("click", this.autoSize,  this);
8954         }
8955         */
8956         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8957             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8958         }
8959         
8960         if (typeof(this.before) == 'object') {
8961             this.before.render(this.el.select('.roo-input-before',true).first());
8962         }
8963         if (typeof(this.after) == 'object') {
8964             this.after.render(this.el.select('.roo-input-after',true).first());
8965         }
8966         
8967         
8968     },
8969     filterValidation : function(e){
8970         if(!e.isNavKeyPress()){
8971             this.validationTask.delay(this.validationDelay);
8972         }
8973     },
8974      /**
8975      * Validates the field value
8976      * @return {Boolean} True if the value is valid, else false
8977      */
8978     validate : function(){
8979         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8980         if(this.disabled || this.validateValue(this.getRawValue())){
8981             this.markValid();
8982             return true;
8983         }
8984         
8985         this.markInvalid();
8986         return false;
8987     },
8988     
8989     
8990     /**
8991      * Validates a value according to the field's validation rules and marks the field as invalid
8992      * if the validation fails
8993      * @param {Mixed} value The value to validate
8994      * @return {Boolean} True if the value is valid, else false
8995      */
8996     validateValue : function(value){
8997         if(value.length < 1)  { // if it's blank
8998             if(this.allowBlank){
8999                 return true;
9000             }            
9001             return this.inputEl().hasClass('hide') ? true : false;
9002         }
9003         
9004         if(value.length < this.minLength){
9005             return false;
9006         }
9007         if(value.length > this.maxLength){
9008             return false;
9009         }
9010         if(this.vtype){
9011             var vt = Roo.form.VTypes;
9012             if(!vt[this.vtype](value, this)){
9013                 return false;
9014             }
9015         }
9016         if(typeof this.validator == "function"){
9017             var msg = this.validator(value);
9018             if(msg !== true){
9019                 return false;
9020             }
9021             if (typeof(msg) == 'string') {
9022                 this.invalidText = msg;
9023             }
9024         }
9025         
9026         if(this.regex && !this.regex.test(value)){
9027             return false;
9028         }
9029         
9030         return true;
9031     },
9032
9033     
9034     
9035      // private
9036     fireKey : function(e){
9037         //Roo.log('field ' + e.getKey());
9038         if(e.isNavKeyPress()){
9039             this.fireEvent("specialkey", this, e);
9040         }
9041     },
9042     focus : function (selectText){
9043         if(this.rendered){
9044             this.inputEl().focus();
9045             if(selectText === true){
9046                 this.inputEl().dom.select();
9047             }
9048         }
9049         return this;
9050     } ,
9051     
9052     onFocus : function(){
9053         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9054            // this.el.addClass(this.focusClass);
9055         }
9056         if(!this.hasFocus){
9057             this.hasFocus = true;
9058             this.startValue = this.getValue();
9059             this.fireEvent("focus", this);
9060         }
9061     },
9062     
9063     beforeBlur : Roo.emptyFn,
9064
9065     
9066     // private
9067     onBlur : function(){
9068         this.beforeBlur();
9069         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9070             //this.el.removeClass(this.focusClass);
9071         }
9072         this.hasFocus = false;
9073         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9074             this.validate();
9075         }
9076         var v = this.getValue();
9077         if(String(v) !== String(this.startValue)){
9078             this.fireEvent('change', this, v, this.startValue);
9079         }
9080         this.fireEvent("blur", this);
9081     },
9082     
9083     /**
9084      * Resets the current field value to the originally loaded value and clears any validation messages
9085      */
9086     reset : function(){
9087         this.setValue(this.originalValue);
9088         this.validate();
9089     },
9090      /**
9091      * Returns the name of the field
9092      * @return {Mixed} name The name field
9093      */
9094     getName: function(){
9095         return this.name;
9096     },
9097      /**
9098      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9099      * @return {Mixed} value The field value
9100      */
9101     getValue : function(){
9102         
9103         var v = this.inputEl().getValue();
9104         
9105         return v;
9106     },
9107     /**
9108      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9109      * @return {Mixed} value The field value
9110      */
9111     getRawValue : function(){
9112         var v = this.inputEl().getValue();
9113         
9114         return v;
9115     },
9116     
9117     /**
9118      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9119      * @param {Mixed} value The value to set
9120      */
9121     setRawValue : function(v){
9122         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9123     },
9124     
9125     selectText : function(start, end){
9126         var v = this.getRawValue();
9127         if(v.length > 0){
9128             start = start === undefined ? 0 : start;
9129             end = end === undefined ? v.length : end;
9130             var d = this.inputEl().dom;
9131             if(d.setSelectionRange){
9132                 d.setSelectionRange(start, end);
9133             }else if(d.createTextRange){
9134                 var range = d.createTextRange();
9135                 range.moveStart("character", start);
9136                 range.moveEnd("character", v.length-end);
9137                 range.select();
9138             }
9139         }
9140     },
9141     
9142     /**
9143      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9144      * @param {Mixed} value The value to set
9145      */
9146     setValue : function(v){
9147         this.value = v;
9148         if(this.rendered){
9149             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9150             this.validate();
9151         }
9152     },
9153     
9154     /*
9155     processValue : function(value){
9156         if(this.stripCharsRe){
9157             var newValue = value.replace(this.stripCharsRe, '');
9158             if(newValue !== value){
9159                 this.setRawValue(newValue);
9160                 return newValue;
9161             }
9162         }
9163         return value;
9164     },
9165   */
9166     preFocus : function(){
9167         
9168         if(this.selectOnFocus){
9169             this.inputEl().dom.select();
9170         }
9171     },
9172     filterKeys : function(e){
9173         var k = e.getKey();
9174         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9175             return;
9176         }
9177         var c = e.getCharCode(), cc = String.fromCharCode(c);
9178         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9179             return;
9180         }
9181         if(!this.maskRe.test(cc)){
9182             e.stopEvent();
9183         }
9184     },
9185      /**
9186      * Clear any invalid styles/messages for this field
9187      */
9188     clearInvalid : function(){
9189         
9190         if(!this.el || this.preventMark){ // not rendered
9191             return;
9192         }
9193         
9194      
9195         this.el.removeClass(this.invalidClass);
9196         
9197         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9198             
9199             var feedback = this.el.select('.form-control-feedback', true).first();
9200             
9201             if(feedback){
9202                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9203             }
9204             
9205         }
9206         
9207         this.fireEvent('valid', this);
9208     },
9209     
9210      /**
9211      * Mark this field as valid
9212      */
9213     markValid : function()
9214     {
9215         if(!this.el  || this.preventMark){ // not rendered...
9216             return;
9217         }
9218         
9219         this.el.removeClass([this.invalidClass, this.validClass]);
9220         
9221         var feedback = this.el.select('.form-control-feedback', true).first();
9222             
9223         if(feedback){
9224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9225         }
9226
9227         if(this.disabled){
9228             return;
9229         }
9230         
9231         if(this.allowBlank && !this.getRawValue().length){
9232             return;
9233         }
9234         
9235         if(this.indicator){
9236             this.indicator.removeClass('visible');
9237             this.indicator.addClass('invisible');
9238         }
9239         
9240         this.el.addClass(this.validClass);
9241         
9242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9243             
9244             var feedback = this.el.select('.form-control-feedback', true).first();
9245             
9246             if(feedback){
9247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9248                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9249             }
9250             
9251         }
9252         
9253         this.fireEvent('valid', this);
9254     },
9255     
9256      /**
9257      * Mark this field as invalid
9258      * @param {String} msg The validation message
9259      */
9260     markInvalid : function(msg)
9261     {
9262         if(!this.el  || this.preventMark){ // not rendered
9263             return;
9264         }
9265         
9266         this.el.removeClass([this.invalidClass, this.validClass]);
9267         
9268         var feedback = this.el.select('.form-control-feedback', true).first();
9269             
9270         if(feedback){
9271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272         }
9273
9274         if(this.disabled){
9275             return;
9276         }
9277         
9278         if(this.allowBlank && !this.getRawValue().length){
9279             return;
9280         }
9281         
9282         if(this.indicator){
9283             this.indicator.removeClass('invisible');
9284             this.indicator.addClass('visible');
9285         }
9286         
9287         this.el.addClass(this.invalidClass);
9288         
9289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9290             
9291             var feedback = this.el.select('.form-control-feedback', true).first();
9292             
9293             if(feedback){
9294                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9295                 
9296                 if(this.getValue().length || this.forceFeedback){
9297                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9298                 }
9299                 
9300             }
9301             
9302         }
9303         
9304         this.fireEvent('invalid', this, msg);
9305     },
9306     // private
9307     SafariOnKeyDown : function(event)
9308     {
9309         // this is a workaround for a password hang bug on chrome/ webkit.
9310         if (this.inputEl().dom.type != 'password') {
9311             return;
9312         }
9313         
9314         var isSelectAll = false;
9315         
9316         if(this.inputEl().dom.selectionEnd > 0){
9317             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9318         }
9319         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9320             event.preventDefault();
9321             this.setValue('');
9322             return;
9323         }
9324         
9325         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9326             
9327             event.preventDefault();
9328             // this is very hacky as keydown always get's upper case.
9329             //
9330             var cc = String.fromCharCode(event.getCharCode());
9331             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9332             
9333         }
9334     },
9335     adjustWidth : function(tag, w){
9336         tag = tag.toLowerCase();
9337         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9338             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9339                 if(tag == 'input'){
9340                     return w + 2;
9341                 }
9342                 if(tag == 'textarea'){
9343                     return w-2;
9344                 }
9345             }else if(Roo.isOpera){
9346                 if(tag == 'input'){
9347                     return w + 2;
9348                 }
9349                 if(tag == 'textarea'){
9350                     return w-2;
9351                 }
9352             }
9353         }
9354         return w;
9355     },
9356     
9357     setFieldLabel : function(v)
9358     {
9359         if(!this.rendered){
9360             return;
9361         }
9362         
9363         this.fieldLabel = v;
9364         
9365         if(this.indicator){
9366             var ar = this.el.select('label > span',true);
9367             if (!ar.length) {
9368                 Roo.log("could not find label > span on element");
9369                 Roo.log(this);
9370                 return;
9371             }
9372             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9373             return;
9374         }
9375         
9376         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9377     }
9378 });
9379
9380  
9381 /*
9382  * - LGPL
9383  *
9384  * Input
9385  * 
9386  */
9387
9388 /**
9389  * @class Roo.bootstrap.TextArea
9390  * @extends Roo.bootstrap.Input
9391  * Bootstrap TextArea class
9392  * @cfg {Number} cols Specifies the visible width of a text area
9393  * @cfg {Number} rows Specifies the visible number of lines in a text area
9394  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9395  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9396  * @cfg {string} html text
9397  * 
9398  * @constructor
9399  * Create a new TextArea
9400  * @param {Object} config The config object
9401  */
9402
9403 Roo.bootstrap.TextArea = function(config){
9404     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9405    
9406 };
9407
9408 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9409      
9410     cols : false,
9411     rows : 5,
9412     readOnly : false,
9413     warp : 'soft',
9414     resize : false,
9415     value: false,
9416     html: false,
9417     
9418     getAutoCreate : function(){
9419         
9420         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9421         
9422         var id = Roo.id();
9423         
9424         var cfg = {};
9425         
9426         if(this.inputType != 'hidden'){
9427             cfg.cls = 'form-group' //input-group
9428         }
9429         
9430         var input =  {
9431             tag: 'textarea',
9432             id : id,
9433             warp : this.warp,
9434             rows : this.rows,
9435             value : this.value || '',
9436             html: this.html || '',
9437             cls : 'form-control',
9438             placeholder : this.placeholder || '' 
9439             
9440         };
9441         
9442         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9443             input.maxLength = this.maxLength;
9444         }
9445         
9446         if(this.resize){
9447             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9448         }
9449         
9450         if(this.cols){
9451             input.cols = this.cols;
9452         }
9453         
9454         if (this.readOnly) {
9455             input.readonly = true;
9456         }
9457         
9458         if (this.name) {
9459             input.name = this.name;
9460         }
9461         
9462         if (this.size) {
9463             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9464         }
9465         
9466         var settings=this;
9467         ['xs','sm','md','lg'].map(function(size){
9468             if (settings[size]) {
9469                 cfg.cls += ' col-' + size + '-' + settings[size];
9470             }
9471         });
9472         
9473         var inputblock = input;
9474         
9475         if(this.hasFeedback && !this.allowBlank){
9476             
9477             var feedback = {
9478                 tag: 'span',
9479                 cls: 'glyphicon form-control-feedback'
9480             };
9481
9482             inputblock = {
9483                 cls : 'has-feedback',
9484                 cn :  [
9485                     input,
9486                     feedback
9487                 ] 
9488             };  
9489         }
9490         
9491         
9492         if (this.before || this.after) {
9493             
9494             inputblock = {
9495                 cls : 'input-group',
9496                 cn :  [] 
9497             };
9498             if (this.before) {
9499                 inputblock.cn.push({
9500                     tag :'span',
9501                     cls : 'input-group-addon',
9502                     html : this.before
9503                 });
9504             }
9505             
9506             inputblock.cn.push(input);
9507             
9508             if(this.hasFeedback && !this.allowBlank){
9509                 inputblock.cls += ' has-feedback';
9510                 inputblock.cn.push(feedback);
9511             }
9512             
9513             if (this.after) {
9514                 inputblock.cn.push({
9515                     tag :'span',
9516                     cls : 'input-group-addon',
9517                     html : this.after
9518                 });
9519             }
9520             
9521         }
9522         
9523         if (align ==='left' && this.fieldLabel.length) {
9524             cfg.cn = [
9525                 {
9526                     tag: 'label',
9527                     'for' :  id,
9528                     cls : 'control-label',
9529                     html : this.fieldLabel
9530                 },
9531                 {
9532                     cls : "",
9533                     cn: [
9534                         inputblock
9535                     ]
9536                 }
9537
9538             ];
9539             
9540             if(this.labelWidth > 12){
9541                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9542             }
9543
9544             if(this.labelWidth < 13 && this.labelmd == 0){
9545                 this.labelmd = this.labelWidth;
9546             }
9547
9548             if(this.labellg > 0){
9549                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9550                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9551             }
9552
9553             if(this.labelmd > 0){
9554                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9555                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9556             }
9557
9558             if(this.labelsm > 0){
9559                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9560                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9561             }
9562
9563             if(this.labelxs > 0){
9564                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9565                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9566             }
9567             
9568         } else if ( this.fieldLabel.length) {
9569             cfg.cn = [
9570
9571                {
9572                    tag: 'label',
9573                    //cls : 'input-group-addon',
9574                    html : this.fieldLabel
9575
9576                },
9577
9578                inputblock
9579
9580            ];
9581
9582         } else {
9583
9584             cfg.cn = [
9585
9586                 inputblock
9587
9588             ];
9589                 
9590         }
9591         
9592         if (this.disabled) {
9593             input.disabled=true;
9594         }
9595         
9596         return cfg;
9597         
9598     },
9599     /**
9600      * return the real textarea element.
9601      */
9602     inputEl: function ()
9603     {
9604         return this.el.select('textarea.form-control',true).first();
9605     },
9606     
9607     /**
9608      * Clear any invalid styles/messages for this field
9609      */
9610     clearInvalid : function()
9611     {
9612         
9613         if(!this.el || this.preventMark){ // not rendered
9614             return;
9615         }
9616         
9617         var label = this.el.select('label', true).first();
9618         var icon = this.el.select('i.fa-star', true).first();
9619         
9620         if(label && icon){
9621             icon.remove();
9622         }
9623         
9624         this.el.removeClass(this.invalidClass);
9625         
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9627             
9628             var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630             if(feedback){
9631                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9632             }
9633             
9634         }
9635         
9636         this.fireEvent('valid', this);
9637     },
9638     
9639      /**
9640      * Mark this field as valid
9641      */
9642     markValid : function()
9643     {
9644         if(!this.el  || this.preventMark){ // not rendered
9645             return;
9646         }
9647         
9648         this.el.removeClass([this.invalidClass, this.validClass]);
9649         
9650         var feedback = this.el.select('.form-control-feedback', true).first();
9651             
9652         if(feedback){
9653             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9654         }
9655
9656         if(this.disabled || this.allowBlank){
9657             return;
9658         }
9659         
9660         var label = this.el.select('label', true).first();
9661         var icon = this.el.select('i.fa-star', true).first();
9662         
9663         if(label && icon){
9664             icon.remove();
9665         }
9666         
9667         this.el.addClass(this.validClass);
9668         
9669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9670             
9671             var feedback = this.el.select('.form-control-feedback', true).first();
9672             
9673             if(feedback){
9674                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9675                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9676             }
9677             
9678         }
9679         
9680         this.fireEvent('valid', this);
9681     },
9682     
9683      /**
9684      * Mark this field as invalid
9685      * @param {String} msg The validation message
9686      */
9687     markInvalid : function(msg)
9688     {
9689         if(!this.el  || this.preventMark){ // not rendered
9690             return;
9691         }
9692         
9693         this.el.removeClass([this.invalidClass, this.validClass]);
9694         
9695         var feedback = this.el.select('.form-control-feedback', true).first();
9696             
9697         if(feedback){
9698             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9699         }
9700
9701         if(this.disabled || this.allowBlank){
9702             return;
9703         }
9704         
9705         var label = this.el.select('label', true).first();
9706         var icon = this.el.select('i.fa-star', true).first();
9707         
9708         if(!this.getValue().length && label && !icon){
9709             this.el.createChild({
9710                 tag : 'i',
9711                 cls : 'text-danger fa fa-lg fa-star',
9712                 tooltip : 'This field is required',
9713                 style : 'margin-right:5px;'
9714             }, label, true);
9715         }
9716
9717         this.el.addClass(this.invalidClass);
9718         
9719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9720             
9721             var feedback = this.el.select('.form-control-feedback', true).first();
9722             
9723             if(feedback){
9724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9725                 
9726                 if(this.getValue().length || this.forceFeedback){
9727                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9728                 }
9729                 
9730             }
9731             
9732         }
9733         
9734         this.fireEvent('invalid', this, msg);
9735     }
9736 });
9737
9738  
9739 /*
9740  * - LGPL
9741  *
9742  * trigger field - base class for combo..
9743  * 
9744  */
9745  
9746 /**
9747  * @class Roo.bootstrap.TriggerField
9748  * @extends Roo.bootstrap.Input
9749  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9750  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9751  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9752  * for which you can provide a custom implementation.  For example:
9753  * <pre><code>
9754 var trigger = new Roo.bootstrap.TriggerField();
9755 trigger.onTriggerClick = myTriggerFn;
9756 trigger.applyTo('my-field');
9757 </code></pre>
9758  *
9759  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9760  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9761  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9762  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9763  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9764
9765  * @constructor
9766  * Create a new TriggerField.
9767  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9768  * to the base TextField)
9769  */
9770 Roo.bootstrap.TriggerField = function(config){
9771     this.mimicing = false;
9772     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9773 };
9774
9775 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9776     /**
9777      * @cfg {String} triggerClass A CSS class to apply to the trigger
9778      */
9779      /**
9780      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9781      */
9782     hideTrigger:false,
9783
9784     /**
9785      * @cfg {Boolean} removable (true|false) special filter default false
9786      */
9787     removable : false,
9788     
9789     /** @cfg {Boolean} grow @hide */
9790     /** @cfg {Number} growMin @hide */
9791     /** @cfg {Number} growMax @hide */
9792
9793     /**
9794      * @hide 
9795      * @method
9796      */
9797     autoSize: Roo.emptyFn,
9798     // private
9799     monitorTab : true,
9800     // private
9801     deferHeight : true,
9802
9803     
9804     actionMode : 'wrap',
9805     
9806     caret : false,
9807     
9808     
9809     getAutoCreate : function(){
9810        
9811         var align = this.labelAlign || this.parentLabelAlign();
9812         
9813         var id = Roo.id();
9814         
9815         var cfg = {
9816             cls: 'form-group' //input-group
9817         };
9818         
9819         
9820         var input =  {
9821             tag: 'input',
9822             id : id,
9823             type : this.inputType,
9824             cls : 'form-control',
9825             autocomplete: 'new-password',
9826             placeholder : this.placeholder || '' 
9827             
9828         };
9829         if (this.name) {
9830             input.name = this.name;
9831         }
9832         if (this.size) {
9833             input.cls += ' input-' + this.size;
9834         }
9835         
9836         if (this.disabled) {
9837             input.disabled=true;
9838         }
9839         
9840         var inputblock = input;
9841         
9842         if(this.hasFeedback && !this.allowBlank){
9843             
9844             var feedback = {
9845                 tag: 'span',
9846                 cls: 'glyphicon form-control-feedback'
9847             };
9848             
9849             if(this.removable && !this.editable && !this.tickable){
9850                 inputblock = {
9851                     cls : 'has-feedback',
9852                     cn :  [
9853                         inputblock,
9854                         {
9855                             tag: 'button',
9856                             html : 'x',
9857                             cls : 'roo-combo-removable-btn close'
9858                         },
9859                         feedback
9860                     ] 
9861                 };
9862             } else {
9863                 inputblock = {
9864                     cls : 'has-feedback',
9865                     cn :  [
9866                         inputblock,
9867                         feedback
9868                     ] 
9869                 };
9870             }
9871
9872         } else {
9873             if(this.removable && !this.editable && !this.tickable){
9874                 inputblock = {
9875                     cls : 'roo-removable',
9876                     cn :  [
9877                         inputblock,
9878                         {
9879                             tag: 'button',
9880                             html : 'x',
9881                             cls : 'roo-combo-removable-btn close'
9882                         }
9883                     ] 
9884                 };
9885             }
9886         }
9887         
9888         if (this.before || this.after) {
9889             
9890             inputblock = {
9891                 cls : 'input-group',
9892                 cn :  [] 
9893             };
9894             if (this.before) {
9895                 inputblock.cn.push({
9896                     tag :'span',
9897                     cls : 'input-group-addon',
9898                     html : this.before
9899                 });
9900             }
9901             
9902             inputblock.cn.push(input);
9903             
9904             if(this.hasFeedback && !this.allowBlank){
9905                 inputblock.cls += ' has-feedback';
9906                 inputblock.cn.push(feedback);
9907             }
9908             
9909             if (this.after) {
9910                 inputblock.cn.push({
9911                     tag :'span',
9912                     cls : 'input-group-addon',
9913                     html : this.after
9914                 });
9915             }
9916             
9917         };
9918         
9919         var box = {
9920             tag: 'div',
9921             cn: [
9922                 {
9923                     tag: 'input',
9924                     type : 'hidden',
9925                     cls: 'form-hidden-field'
9926                 },
9927                 inputblock
9928             ]
9929             
9930         };
9931         
9932         if(this.multiple){
9933             box = {
9934                 tag: 'div',
9935                 cn: [
9936                     {
9937                         tag: 'input',
9938                         type : 'hidden',
9939                         cls: 'form-hidden-field'
9940                     },
9941                     {
9942                         tag: 'ul',
9943                         cls: 'roo-select2-choices',
9944                         cn:[
9945                             {
9946                                 tag: 'li',
9947                                 cls: 'roo-select2-search-field',
9948                                 cn: [
9949
9950                                     inputblock
9951                                 ]
9952                             }
9953                         ]
9954                     }
9955                 ]
9956             }
9957         };
9958         
9959         var combobox = {
9960             cls: 'roo-select2-container input-group',
9961             cn: [
9962                 box
9963 //                {
9964 //                    tag: 'ul',
9965 //                    cls: 'typeahead typeahead-long dropdown-menu',
9966 //                    style: 'display:none'
9967 //                }
9968             ]
9969         };
9970         
9971         if(!this.multiple && this.showToggleBtn){
9972             
9973             var caret = {
9974                         tag: 'span',
9975                         cls: 'caret'
9976              };
9977             if (this.caret != false) {
9978                 caret = {
9979                      tag: 'i',
9980                      cls: 'fa fa-' + this.caret
9981                 };
9982                 
9983             }
9984             
9985             combobox.cn.push({
9986                 tag :'span',
9987                 cls : 'input-group-addon btn dropdown-toggle',
9988                 cn : [
9989                     caret,
9990                     {
9991                         tag: 'span',
9992                         cls: 'combobox-clear',
9993                         cn  : [
9994                             {
9995                                 tag : 'i',
9996                                 cls: 'icon-remove'
9997                             }
9998                         ]
9999                     }
10000                 ]
10001
10002             })
10003         }
10004         
10005         if(this.multiple){
10006             combobox.cls += ' roo-select2-container-multi';
10007         }
10008         
10009         if (align ==='left' && this.fieldLabel.length) {
10010             
10011             cfg.cls += ' roo-form-group-label-left';
10012
10013             cfg.cn = [
10014                 {
10015                     tag : 'i',
10016                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10017                     tooltip : 'This field is required'
10018                 },
10019                 {
10020                     tag: 'label',
10021                     'for' :  id,
10022                     cls : 'control-label',
10023                     html : this.fieldLabel
10024
10025                 },
10026                 {
10027                     cls : "", 
10028                     cn: [
10029                         combobox
10030                     ]
10031                 }
10032
10033             ];
10034             
10035             var labelCfg = cfg.cn[1];
10036             var contentCfg = cfg.cn[2];
10037             
10038             if(this.indicatorpos == 'right'){
10039                 cfg.cn = [
10040                     {
10041                         tag: 'label',
10042                         'for' :  id,
10043                         cls : 'control-label',
10044                         cn : [
10045                             {
10046                                 tag : 'span',
10047                                 html : this.fieldLabel
10048                             },
10049                             {
10050                                 tag : 'i',
10051                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10052                                 tooltip : 'This field is required'
10053                             }
10054                         ]
10055                     },
10056                     {
10057                         cls : "", 
10058                         cn: [
10059                             combobox
10060                         ]
10061                     }
10062
10063                 ];
10064                 
10065                 labelCfg = cfg.cn[0];
10066                 contentCfg = cfg.cn[1];
10067             }
10068             
10069             if(this.labelWidth > 12){
10070                 labelCfg.style = "width: " + this.labelWidth + 'px';
10071             }
10072             
10073             if(this.labelWidth < 13 && this.labelmd == 0){
10074                 this.labelmd = this.labelWidth;
10075             }
10076             
10077             if(this.labellg > 0){
10078                 labelCfg.cls += ' col-lg-' + this.labellg;
10079                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10080             }
10081             
10082             if(this.labelmd > 0){
10083                 labelCfg.cls += ' col-md-' + this.labelmd;
10084                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10085             }
10086             
10087             if(this.labelsm > 0){
10088                 labelCfg.cls += ' col-sm-' + this.labelsm;
10089                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10090             }
10091             
10092             if(this.labelxs > 0){
10093                 labelCfg.cls += ' col-xs-' + this.labelxs;
10094                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10095             }
10096             
10097         } else if ( this.fieldLabel.length) {
10098 //                Roo.log(" label");
10099             cfg.cn = [
10100                 {
10101                    tag : 'i',
10102                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10103                    tooltip : 'This field is required'
10104                },
10105                {
10106                    tag: 'label',
10107                    //cls : 'input-group-addon',
10108                    html : this.fieldLabel
10109
10110                },
10111
10112                combobox
10113
10114             ];
10115             
10116             if(this.indicatorpos == 'right'){
10117                 
10118                 cfg.cn = [
10119                     {
10120                        tag: 'label',
10121                        cn : [
10122                            {
10123                                tag : 'span',
10124                                html : this.fieldLabel
10125                            },
10126                            {
10127                               tag : 'i',
10128                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10129                               tooltip : 'This field is required'
10130                            }
10131                        ]
10132
10133                     },
10134                     combobox
10135
10136                 ];
10137
10138             }
10139
10140         } else {
10141             
10142 //                Roo.log(" no label && no align");
10143                 cfg = combobox
10144                      
10145                 
10146         }
10147         
10148         var settings=this;
10149         ['xs','sm','md','lg'].map(function(size){
10150             if (settings[size]) {
10151                 cfg.cls += ' col-' + size + '-' + settings[size];
10152             }
10153         });
10154         
10155         return cfg;
10156         
10157     },
10158     
10159     
10160     
10161     // private
10162     onResize : function(w, h){
10163 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10164 //        if(typeof w == 'number'){
10165 //            var x = w - this.trigger.getWidth();
10166 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10167 //            this.trigger.setStyle('left', x+'px');
10168 //        }
10169     },
10170
10171     // private
10172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10173
10174     // private
10175     getResizeEl : function(){
10176         return this.inputEl();
10177     },
10178
10179     // private
10180     getPositionEl : function(){
10181         return this.inputEl();
10182     },
10183
10184     // private
10185     alignErrorIcon : function(){
10186         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10187     },
10188
10189     // private
10190     initEvents : function(){
10191         
10192         this.createList();
10193         
10194         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10195         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10196         if(!this.multiple && this.showToggleBtn){
10197             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10198             if(this.hideTrigger){
10199                 this.trigger.setDisplayed(false);
10200             }
10201             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10202         }
10203         
10204         if(this.multiple){
10205             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10206         }
10207         
10208         if(this.removable && !this.editable && !this.tickable){
10209             var close = this.closeTriggerEl();
10210             
10211             if(close){
10212                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10213                 close.on('click', this.removeBtnClick, this, close);
10214             }
10215         }
10216         
10217         //this.trigger.addClassOnOver('x-form-trigger-over');
10218         //this.trigger.addClassOnClick('x-form-trigger-click');
10219         
10220         //if(!this.width){
10221         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10222         //}
10223     },
10224     
10225     closeTriggerEl : function()
10226     {
10227         var close = this.el.select('.roo-combo-removable-btn', true).first();
10228         return close ? close : false;
10229     },
10230     
10231     removeBtnClick : function(e, h, el)
10232     {
10233         e.preventDefault();
10234         
10235         if(this.fireEvent("remove", this) !== false){
10236             this.reset();
10237             this.fireEvent("afterremove", this)
10238         }
10239     },
10240     
10241     createList : function()
10242     {
10243         this.list = Roo.get(document.body).createChild({
10244             tag: 'ul',
10245             cls: 'typeahead typeahead-long dropdown-menu',
10246             style: 'display:none'
10247         });
10248         
10249         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10250         
10251     },
10252
10253     // private
10254     initTrigger : function(){
10255        
10256     },
10257
10258     // private
10259     onDestroy : function(){
10260         if(this.trigger){
10261             this.trigger.removeAllListeners();
10262           //  this.trigger.remove();
10263         }
10264         //if(this.wrap){
10265         //    this.wrap.remove();
10266         //}
10267         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10268     },
10269
10270     // private
10271     onFocus : function(){
10272         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10273         /*
10274         if(!this.mimicing){
10275             this.wrap.addClass('x-trigger-wrap-focus');
10276             this.mimicing = true;
10277             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10278             if(this.monitorTab){
10279                 this.el.on("keydown", this.checkTab, this);
10280             }
10281         }
10282         */
10283     },
10284
10285     // private
10286     checkTab : function(e){
10287         if(e.getKey() == e.TAB){
10288             this.triggerBlur();
10289         }
10290     },
10291
10292     // private
10293     onBlur : function(){
10294         // do nothing
10295     },
10296
10297     // private
10298     mimicBlur : function(e, t){
10299         /*
10300         if(!this.wrap.contains(t) && this.validateBlur()){
10301             this.triggerBlur();
10302         }
10303         */
10304     },
10305
10306     // private
10307     triggerBlur : function(){
10308         this.mimicing = false;
10309         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10310         if(this.monitorTab){
10311             this.el.un("keydown", this.checkTab, this);
10312         }
10313         //this.wrap.removeClass('x-trigger-wrap-focus');
10314         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10315     },
10316
10317     // private
10318     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10319     validateBlur : function(e, t){
10320         return true;
10321     },
10322
10323     // private
10324     onDisable : function(){
10325         this.inputEl().dom.disabled = true;
10326         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10327         //if(this.wrap){
10328         //    this.wrap.addClass('x-item-disabled');
10329         //}
10330     },
10331
10332     // private
10333     onEnable : function(){
10334         this.inputEl().dom.disabled = false;
10335         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10336         //if(this.wrap){
10337         //    this.el.removeClass('x-item-disabled');
10338         //}
10339     },
10340
10341     // private
10342     onShow : function(){
10343         var ae = this.getActionEl();
10344         
10345         if(ae){
10346             ae.dom.style.display = '';
10347             ae.dom.style.visibility = 'visible';
10348         }
10349     },
10350
10351     // private
10352     
10353     onHide : function(){
10354         var ae = this.getActionEl();
10355         ae.dom.style.display = 'none';
10356     },
10357
10358     /**
10359      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10360      * by an implementing function.
10361      * @method
10362      * @param {EventObject} e
10363      */
10364     onTriggerClick : Roo.emptyFn
10365 });
10366  /*
10367  * Based on:
10368  * Ext JS Library 1.1.1
10369  * Copyright(c) 2006-2007, Ext JS, LLC.
10370  *
10371  * Originally Released Under LGPL - original licence link has changed is not relivant.
10372  *
10373  * Fork - LGPL
10374  * <script type="text/javascript">
10375  */
10376
10377
10378 /**
10379  * @class Roo.data.SortTypes
10380  * @singleton
10381  * Defines the default sorting (casting?) comparison functions used when sorting data.
10382  */
10383 Roo.data.SortTypes = {
10384     /**
10385      * Default sort that does nothing
10386      * @param {Mixed} s The value being converted
10387      * @return {Mixed} The comparison value
10388      */
10389     none : function(s){
10390         return s;
10391     },
10392     
10393     /**
10394      * The regular expression used to strip tags
10395      * @type {RegExp}
10396      * @property
10397      */
10398     stripTagsRE : /<\/?[^>]+>/gi,
10399     
10400     /**
10401      * Strips all HTML tags to sort on text only
10402      * @param {Mixed} s The value being converted
10403      * @return {String} The comparison value
10404      */
10405     asText : function(s){
10406         return String(s).replace(this.stripTagsRE, "");
10407     },
10408     
10409     /**
10410      * Strips all HTML tags to sort on text only - Case insensitive
10411      * @param {Mixed} s The value being converted
10412      * @return {String} The comparison value
10413      */
10414     asUCText : function(s){
10415         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10416     },
10417     
10418     /**
10419      * Case insensitive string
10420      * @param {Mixed} s The value being converted
10421      * @return {String} The comparison value
10422      */
10423     asUCString : function(s) {
10424         return String(s).toUpperCase();
10425     },
10426     
10427     /**
10428      * Date sorting
10429      * @param {Mixed} s The value being converted
10430      * @return {Number} The comparison value
10431      */
10432     asDate : function(s) {
10433         if(!s){
10434             return 0;
10435         }
10436         if(s instanceof Date){
10437             return s.getTime();
10438         }
10439         return Date.parse(String(s));
10440     },
10441     
10442     /**
10443      * Float sorting
10444      * @param {Mixed} s The value being converted
10445      * @return {Float} The comparison value
10446      */
10447     asFloat : function(s) {
10448         var val = parseFloat(String(s).replace(/,/g, ""));
10449         if(isNaN(val)) {
10450             val = 0;
10451         }
10452         return val;
10453     },
10454     
10455     /**
10456      * Integer sorting
10457      * @param {Mixed} s The value being converted
10458      * @return {Number} The comparison value
10459      */
10460     asInt : function(s) {
10461         var val = parseInt(String(s).replace(/,/g, ""));
10462         if(isNaN(val)) {
10463             val = 0;
10464         }
10465         return val;
10466     }
10467 };/*
10468  * Based on:
10469  * Ext JS Library 1.1.1
10470  * Copyright(c) 2006-2007, Ext JS, LLC.
10471  *
10472  * Originally Released Under LGPL - original licence link has changed is not relivant.
10473  *
10474  * Fork - LGPL
10475  * <script type="text/javascript">
10476  */
10477
10478 /**
10479 * @class Roo.data.Record
10480  * Instances of this class encapsulate both record <em>definition</em> information, and record
10481  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10482  * to access Records cached in an {@link Roo.data.Store} object.<br>
10483  * <p>
10484  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10485  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10486  * objects.<br>
10487  * <p>
10488  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10489  * @constructor
10490  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10491  * {@link #create}. The parameters are the same.
10492  * @param {Array} data An associative Array of data values keyed by the field name.
10493  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10494  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10495  * not specified an integer id is generated.
10496  */
10497 Roo.data.Record = function(data, id){
10498     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10499     this.data = data;
10500 };
10501
10502 /**
10503  * Generate a constructor for a specific record layout.
10504  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10505  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10506  * Each field definition object may contain the following properties: <ul>
10507  * <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,
10508  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10509  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10510  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10511  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10512  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10513  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10514  * this may be omitted.</p></li>
10515  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10516  * <ul><li>auto (Default, implies no conversion)</li>
10517  * <li>string</li>
10518  * <li>int</li>
10519  * <li>float</li>
10520  * <li>boolean</li>
10521  * <li>date</li></ul></p></li>
10522  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10523  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10524  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10525  * by the Reader into an object that will be stored in the Record. It is passed the
10526  * following parameters:<ul>
10527  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10528  * </ul></p></li>
10529  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10530  * </ul>
10531  * <br>usage:<br><pre><code>
10532 var TopicRecord = Roo.data.Record.create(
10533     {name: 'title', mapping: 'topic_title'},
10534     {name: 'author', mapping: 'username'},
10535     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10536     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10537     {name: 'lastPoster', mapping: 'user2'},
10538     {name: 'excerpt', mapping: 'post_text'}
10539 );
10540
10541 var myNewRecord = new TopicRecord({
10542     title: 'Do my job please',
10543     author: 'noobie',
10544     totalPosts: 1,
10545     lastPost: new Date(),
10546     lastPoster: 'Animal',
10547     excerpt: 'No way dude!'
10548 });
10549 myStore.add(myNewRecord);
10550 </code></pre>
10551  * @method create
10552  * @static
10553  */
10554 Roo.data.Record.create = function(o){
10555     var f = function(){
10556         f.superclass.constructor.apply(this, arguments);
10557     };
10558     Roo.extend(f, Roo.data.Record);
10559     var p = f.prototype;
10560     p.fields = new Roo.util.MixedCollection(false, function(field){
10561         return field.name;
10562     });
10563     for(var i = 0, len = o.length; i < len; i++){
10564         p.fields.add(new Roo.data.Field(o[i]));
10565     }
10566     f.getField = function(name){
10567         return p.fields.get(name);  
10568     };
10569     return f;
10570 };
10571
10572 Roo.data.Record.AUTO_ID = 1000;
10573 Roo.data.Record.EDIT = 'edit';
10574 Roo.data.Record.REJECT = 'reject';
10575 Roo.data.Record.COMMIT = 'commit';
10576
10577 Roo.data.Record.prototype = {
10578     /**
10579      * Readonly flag - true if this record has been modified.
10580      * @type Boolean
10581      */
10582     dirty : false,
10583     editing : false,
10584     error: null,
10585     modified: null,
10586
10587     // private
10588     join : function(store){
10589         this.store = store;
10590     },
10591
10592     /**
10593      * Set the named field to the specified value.
10594      * @param {String} name The name of the field to set.
10595      * @param {Object} value The value to set the field to.
10596      */
10597     set : function(name, value){
10598         if(this.data[name] == value){
10599             return;
10600         }
10601         this.dirty = true;
10602         if(!this.modified){
10603             this.modified = {};
10604         }
10605         if(typeof this.modified[name] == 'undefined'){
10606             this.modified[name] = this.data[name];
10607         }
10608         this.data[name] = value;
10609         if(!this.editing && this.store){
10610             this.store.afterEdit(this);
10611         }       
10612     },
10613
10614     /**
10615      * Get the value of the named field.
10616      * @param {String} name The name of the field to get the value of.
10617      * @return {Object} The value of the field.
10618      */
10619     get : function(name){
10620         return this.data[name]; 
10621     },
10622
10623     // private
10624     beginEdit : function(){
10625         this.editing = true;
10626         this.modified = {}; 
10627     },
10628
10629     // private
10630     cancelEdit : function(){
10631         this.editing = false;
10632         delete this.modified;
10633     },
10634
10635     // private
10636     endEdit : function(){
10637         this.editing = false;
10638         if(this.dirty && this.store){
10639             this.store.afterEdit(this);
10640         }
10641     },
10642
10643     /**
10644      * Usually called by the {@link Roo.data.Store} which owns the Record.
10645      * Rejects all changes made to the Record since either creation, or the last commit operation.
10646      * Modified fields are reverted to their original values.
10647      * <p>
10648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10649      * of reject operations.
10650      */
10651     reject : function(){
10652         var m = this.modified;
10653         for(var n in m){
10654             if(typeof m[n] != "function"){
10655                 this.data[n] = m[n];
10656             }
10657         }
10658         this.dirty = false;
10659         delete this.modified;
10660         this.editing = false;
10661         if(this.store){
10662             this.store.afterReject(this);
10663         }
10664     },
10665
10666     /**
10667      * Usually called by the {@link Roo.data.Store} which owns the Record.
10668      * Commits all changes made to the Record since either creation, or the last commit operation.
10669      * <p>
10670      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10671      * of commit operations.
10672      */
10673     commit : function(){
10674         this.dirty = false;
10675         delete this.modified;
10676         this.editing = false;
10677         if(this.store){
10678             this.store.afterCommit(this);
10679         }
10680     },
10681
10682     // private
10683     hasError : function(){
10684         return this.error != null;
10685     },
10686
10687     // private
10688     clearError : function(){
10689         this.error = null;
10690     },
10691
10692     /**
10693      * Creates a copy of this record.
10694      * @param {String} id (optional) A new record id if you don't want to use this record's id
10695      * @return {Record}
10696      */
10697     copy : function(newId) {
10698         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10699     }
10700 };/*
10701  * Based on:
10702  * Ext JS Library 1.1.1
10703  * Copyright(c) 2006-2007, Ext JS, LLC.
10704  *
10705  * Originally Released Under LGPL - original licence link has changed is not relivant.
10706  *
10707  * Fork - LGPL
10708  * <script type="text/javascript">
10709  */
10710
10711
10712
10713 /**
10714  * @class Roo.data.Store
10715  * @extends Roo.util.Observable
10716  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10717  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10718  * <p>
10719  * 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
10720  * has no knowledge of the format of the data returned by the Proxy.<br>
10721  * <p>
10722  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10723  * instances from the data object. These records are cached and made available through accessor functions.
10724  * @constructor
10725  * Creates a new Store.
10726  * @param {Object} config A config object containing the objects needed for the Store to access data,
10727  * and read the data into Records.
10728  */
10729 Roo.data.Store = function(config){
10730     this.data = new Roo.util.MixedCollection(false);
10731     this.data.getKey = function(o){
10732         return o.id;
10733     };
10734     this.baseParams = {};
10735     // private
10736     this.paramNames = {
10737         "start" : "start",
10738         "limit" : "limit",
10739         "sort" : "sort",
10740         "dir" : "dir",
10741         "multisort" : "_multisort"
10742     };
10743
10744     if(config && config.data){
10745         this.inlineData = config.data;
10746         delete config.data;
10747     }
10748
10749     Roo.apply(this, config);
10750     
10751     if(this.reader){ // reader passed
10752         this.reader = Roo.factory(this.reader, Roo.data);
10753         this.reader.xmodule = this.xmodule || false;
10754         if(!this.recordType){
10755             this.recordType = this.reader.recordType;
10756         }
10757         if(this.reader.onMetaChange){
10758             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10759         }
10760     }
10761
10762     if(this.recordType){
10763         this.fields = this.recordType.prototype.fields;
10764     }
10765     this.modified = [];
10766
10767     this.addEvents({
10768         /**
10769          * @event datachanged
10770          * Fires when the data cache has changed, and a widget which is using this Store
10771          * as a Record cache should refresh its view.
10772          * @param {Store} this
10773          */
10774         datachanged : true,
10775         /**
10776          * @event metachange
10777          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10778          * @param {Store} this
10779          * @param {Object} meta The JSON metadata
10780          */
10781         metachange : true,
10782         /**
10783          * @event add
10784          * Fires when Records have been added to the Store
10785          * @param {Store} this
10786          * @param {Roo.data.Record[]} records The array of Records added
10787          * @param {Number} index The index at which the record(s) were added
10788          */
10789         add : true,
10790         /**
10791          * @event remove
10792          * Fires when a Record has been removed from the Store
10793          * @param {Store} this
10794          * @param {Roo.data.Record} record The Record that was removed
10795          * @param {Number} index The index at which the record was removed
10796          */
10797         remove : true,
10798         /**
10799          * @event update
10800          * Fires when a Record has been updated
10801          * @param {Store} this
10802          * @param {Roo.data.Record} record The Record that was updated
10803          * @param {String} operation The update operation being performed.  Value may be one of:
10804          * <pre><code>
10805  Roo.data.Record.EDIT
10806  Roo.data.Record.REJECT
10807  Roo.data.Record.COMMIT
10808          * </code></pre>
10809          */
10810         update : true,
10811         /**
10812          * @event clear
10813          * Fires when the data cache has been cleared.
10814          * @param {Store} this
10815          */
10816         clear : true,
10817         /**
10818          * @event beforeload
10819          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10820          * the load action will be canceled.
10821          * @param {Store} this
10822          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10823          */
10824         beforeload : true,
10825         /**
10826          * @event beforeloadadd
10827          * Fires after a new set of Records has been loaded.
10828          * @param {Store} this
10829          * @param {Roo.data.Record[]} records The Records that were loaded
10830          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10831          */
10832         beforeloadadd : true,
10833         /**
10834          * @event load
10835          * Fires after a new set of Records has been loaded, before they are added to the store.
10836          * @param {Store} this
10837          * @param {Roo.data.Record[]} records The Records that were loaded
10838          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10839          * @params {Object} return from reader
10840          */
10841         load : true,
10842         /**
10843          * @event loadexception
10844          * Fires if an exception occurs in the Proxy during loading.
10845          * Called with the signature of the Proxy's "loadexception" event.
10846          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10847          * 
10848          * @param {Proxy} 
10849          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10850          * @param {Object} load options 
10851          * @param {Object} jsonData from your request (normally this contains the Exception)
10852          */
10853         loadexception : true
10854     });
10855     
10856     if(this.proxy){
10857         this.proxy = Roo.factory(this.proxy, Roo.data);
10858         this.proxy.xmodule = this.xmodule || false;
10859         this.relayEvents(this.proxy,  ["loadexception"]);
10860     }
10861     this.sortToggle = {};
10862     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10863
10864     Roo.data.Store.superclass.constructor.call(this);
10865
10866     if(this.inlineData){
10867         this.loadData(this.inlineData);
10868         delete this.inlineData;
10869     }
10870 };
10871
10872 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10873      /**
10874     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10875     * without a remote query - used by combo/forms at present.
10876     */
10877     
10878     /**
10879     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10880     */
10881     /**
10882     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10883     */
10884     /**
10885     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10886     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10887     */
10888     /**
10889     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10890     * on any HTTP request
10891     */
10892     /**
10893     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10894     */
10895     /**
10896     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10897     */
10898     multiSort: false,
10899     /**
10900     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10901     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10902     */
10903     remoteSort : false,
10904
10905     /**
10906     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10907      * loaded or when a record is removed. (defaults to false).
10908     */
10909     pruneModifiedRecords : false,
10910
10911     // private
10912     lastOptions : null,
10913
10914     /**
10915      * Add Records to the Store and fires the add event.
10916      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10917      */
10918     add : function(records){
10919         records = [].concat(records);
10920         for(var i = 0, len = records.length; i < len; i++){
10921             records[i].join(this);
10922         }
10923         var index = this.data.length;
10924         this.data.addAll(records);
10925         this.fireEvent("add", this, records, index);
10926     },
10927
10928     /**
10929      * Remove a Record from the Store and fires the remove event.
10930      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10931      */
10932     remove : function(record){
10933         var index = this.data.indexOf(record);
10934         this.data.removeAt(index);
10935         if(this.pruneModifiedRecords){
10936             this.modified.remove(record);
10937         }
10938         this.fireEvent("remove", this, record, index);
10939     },
10940
10941     /**
10942      * Remove all Records from the Store and fires the clear event.
10943      */
10944     removeAll : function(){
10945         this.data.clear();
10946         if(this.pruneModifiedRecords){
10947             this.modified = [];
10948         }
10949         this.fireEvent("clear", this);
10950     },
10951
10952     /**
10953      * Inserts Records to the Store at the given index and fires the add event.
10954      * @param {Number} index The start index at which to insert the passed Records.
10955      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10956      */
10957     insert : function(index, records){
10958         records = [].concat(records);
10959         for(var i = 0, len = records.length; i < len; i++){
10960             this.data.insert(index, records[i]);
10961             records[i].join(this);
10962         }
10963         this.fireEvent("add", this, records, index);
10964     },
10965
10966     /**
10967      * Get the index within the cache of the passed Record.
10968      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10969      * @return {Number} The index of the passed Record. Returns -1 if not found.
10970      */
10971     indexOf : function(record){
10972         return this.data.indexOf(record);
10973     },
10974
10975     /**
10976      * Get the index within the cache of the Record with the passed id.
10977      * @param {String} id The id of the Record to find.
10978      * @return {Number} The index of the Record. Returns -1 if not found.
10979      */
10980     indexOfId : function(id){
10981         return this.data.indexOfKey(id);
10982     },
10983
10984     /**
10985      * Get the Record with the specified id.
10986      * @param {String} id The id of the Record to find.
10987      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10988      */
10989     getById : function(id){
10990         return this.data.key(id);
10991     },
10992
10993     /**
10994      * Get the Record at the specified index.
10995      * @param {Number} index The index of the Record to find.
10996      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10997      */
10998     getAt : function(index){
10999         return this.data.itemAt(index);
11000     },
11001
11002     /**
11003      * Returns a range of Records between specified indices.
11004      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11005      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11006      * @return {Roo.data.Record[]} An array of Records
11007      */
11008     getRange : function(start, end){
11009         return this.data.getRange(start, end);
11010     },
11011
11012     // private
11013     storeOptions : function(o){
11014         o = Roo.apply({}, o);
11015         delete o.callback;
11016         delete o.scope;
11017         this.lastOptions = o;
11018     },
11019
11020     /**
11021      * Loads the Record cache from the configured Proxy using the configured Reader.
11022      * <p>
11023      * If using remote paging, then the first load call must specify the <em>start</em>
11024      * and <em>limit</em> properties in the options.params property to establish the initial
11025      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11026      * <p>
11027      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11028      * and this call will return before the new data has been loaded. Perform any post-processing
11029      * in a callback function, or in a "load" event handler.</strong>
11030      * <p>
11031      * @param {Object} options An object containing properties which control loading options:<ul>
11032      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11033      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11034      * passed the following arguments:<ul>
11035      * <li>r : Roo.data.Record[]</li>
11036      * <li>options: Options object from the load call</li>
11037      * <li>success: Boolean success indicator</li></ul></li>
11038      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11039      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11040      * </ul>
11041      */
11042     load : function(options){
11043         options = options || {};
11044         if(this.fireEvent("beforeload", this, options) !== false){
11045             this.storeOptions(options);
11046             var p = Roo.apply(options.params || {}, this.baseParams);
11047             // if meta was not loaded from remote source.. try requesting it.
11048             if (!this.reader.metaFromRemote) {
11049                 p._requestMeta = 1;
11050             }
11051             if(this.sortInfo && this.remoteSort){
11052                 var pn = this.paramNames;
11053                 p[pn["sort"]] = this.sortInfo.field;
11054                 p[pn["dir"]] = this.sortInfo.direction;
11055             }
11056             if (this.multiSort) {
11057                 var pn = this.paramNames;
11058                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11059             }
11060             
11061             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11062         }
11063     },
11064
11065     /**
11066      * Reloads the Record cache from the configured Proxy using the configured Reader and
11067      * the options from the last load operation performed.
11068      * @param {Object} options (optional) An object containing properties which may override the options
11069      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11070      * the most recently used options are reused).
11071      */
11072     reload : function(options){
11073         this.load(Roo.applyIf(options||{}, this.lastOptions));
11074     },
11075
11076     // private
11077     // Called as a callback by the Reader during a load operation.
11078     loadRecords : function(o, options, success){
11079         if(!o || success === false){
11080             if(success !== false){
11081                 this.fireEvent("load", this, [], options, o);
11082             }
11083             if(options.callback){
11084                 options.callback.call(options.scope || this, [], options, false);
11085             }
11086             return;
11087         }
11088         // if data returned failure - throw an exception.
11089         if (o.success === false) {
11090             // show a message if no listener is registered.
11091             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11092                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11093             }
11094             // loadmask wil be hooked into this..
11095             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11096             return;
11097         }
11098         var r = o.records, t = o.totalRecords || r.length;
11099         
11100         this.fireEvent("beforeloadadd", this, r, options, o);
11101         
11102         if(!options || options.add !== true){
11103             if(this.pruneModifiedRecords){
11104                 this.modified = [];
11105             }
11106             for(var i = 0, len = r.length; i < len; i++){
11107                 r[i].join(this);
11108             }
11109             if(this.snapshot){
11110                 this.data = this.snapshot;
11111                 delete this.snapshot;
11112             }
11113             this.data.clear();
11114             this.data.addAll(r);
11115             this.totalLength = t;
11116             this.applySort();
11117             this.fireEvent("datachanged", this);
11118         }else{
11119             this.totalLength = Math.max(t, this.data.length+r.length);
11120             this.add(r);
11121         }
11122         
11123         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11124                 
11125             var e = new Roo.data.Record({});
11126
11127             e.set(this.parent.displayField, this.parent.emptyTitle);
11128             e.set(this.parent.valueField, '');
11129
11130             this.insert(0, e);
11131         }
11132             
11133         this.fireEvent("load", this, r, options, o);
11134         if(options.callback){
11135             options.callback.call(options.scope || this, r, options, true);
11136         }
11137     },
11138
11139
11140     /**
11141      * Loads data from a passed data block. A Reader which understands the format of the data
11142      * must have been configured in the constructor.
11143      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11144      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11145      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11146      */
11147     loadData : function(o, append){
11148         var r = this.reader.readRecords(o);
11149         this.loadRecords(r, {add: append}, true);
11150     },
11151
11152     /**
11153      * Gets the number of cached records.
11154      * <p>
11155      * <em>If using paging, this may not be the total size of the dataset. If the data object
11156      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11157      * the data set size</em>
11158      */
11159     getCount : function(){
11160         return this.data.length || 0;
11161     },
11162
11163     /**
11164      * Gets the total number of records in the dataset as returned by the server.
11165      * <p>
11166      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11167      * the dataset size</em>
11168      */
11169     getTotalCount : function(){
11170         return this.totalLength || 0;
11171     },
11172
11173     /**
11174      * Returns the sort state of the Store as an object with two properties:
11175      * <pre><code>
11176  field {String} The name of the field by which the Records are sorted
11177  direction {String} The sort order, "ASC" or "DESC"
11178      * </code></pre>
11179      */
11180     getSortState : function(){
11181         return this.sortInfo;
11182     },
11183
11184     // private
11185     applySort : function(){
11186         if(this.sortInfo && !this.remoteSort){
11187             var s = this.sortInfo, f = s.field;
11188             var st = this.fields.get(f).sortType;
11189             var fn = function(r1, r2){
11190                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11191                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11192             };
11193             this.data.sort(s.direction, fn);
11194             if(this.snapshot && this.snapshot != this.data){
11195                 this.snapshot.sort(s.direction, fn);
11196             }
11197         }
11198     },
11199
11200     /**
11201      * Sets the default sort column and order to be used by the next load operation.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     setDefaultSort : function(field, dir){
11206         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11207     },
11208
11209     /**
11210      * Sort the Records.
11211      * If remote sorting is used, the sort is performed on the server, and the cache is
11212      * reloaded. If local sorting is used, the cache is sorted internally.
11213      * @param {String} fieldName The name of the field to sort by.
11214      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11215      */
11216     sort : function(fieldName, dir){
11217         var f = this.fields.get(fieldName);
11218         if(!dir){
11219             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11220             
11221             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11222                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11223             }else{
11224                 dir = f.sortDir;
11225             }
11226         }
11227         this.sortToggle[f.name] = dir;
11228         this.sortInfo = {field: f.name, direction: dir};
11229         if(!this.remoteSort){
11230             this.applySort();
11231             this.fireEvent("datachanged", this);
11232         }else{
11233             this.load(this.lastOptions);
11234         }
11235     },
11236
11237     /**
11238      * Calls the specified function for each of the Records in the cache.
11239      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11240      * Returning <em>false</em> aborts and exits the iteration.
11241      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11242      */
11243     each : function(fn, scope){
11244         this.data.each(fn, scope);
11245     },
11246
11247     /**
11248      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11249      * (e.g., during paging).
11250      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11251      */
11252     getModifiedRecords : function(){
11253         return this.modified;
11254     },
11255
11256     // private
11257     createFilterFn : function(property, value, anyMatch){
11258         if(!value.exec){ // not a regex
11259             value = String(value);
11260             if(value.length == 0){
11261                 return false;
11262             }
11263             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11264         }
11265         return function(r){
11266             return value.test(r.data[property]);
11267         };
11268     },
11269
11270     /**
11271      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11272      * @param {String} property A field on your records
11273      * @param {Number} start The record index to start at (defaults to 0)
11274      * @param {Number} end The last record index to include (defaults to length - 1)
11275      * @return {Number} The sum
11276      */
11277     sum : function(property, start, end){
11278         var rs = this.data.items, v = 0;
11279         start = start || 0;
11280         end = (end || end === 0) ? end : rs.length-1;
11281
11282         for(var i = start; i <= end; i++){
11283             v += (rs[i].data[property] || 0);
11284         }
11285         return v;
11286     },
11287
11288     /**
11289      * Filter the records by a specified property.
11290      * @param {String} field A field on your records
11291      * @param {String/RegExp} value Either a string that the field
11292      * should start with or a RegExp to test against the field
11293      * @param {Boolean} anyMatch True to match any part not just the beginning
11294      */
11295     filter : function(property, value, anyMatch){
11296         var fn = this.createFilterFn(property, value, anyMatch);
11297         return fn ? this.filterBy(fn) : this.clearFilter();
11298     },
11299
11300     /**
11301      * Filter by a function. The specified function will be called with each
11302      * record in this data source. If the function returns true the record is included,
11303      * otherwise it is filtered.
11304      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11305      * @param {Object} scope (optional) The scope of the function (defaults to this)
11306      */
11307     filterBy : function(fn, scope){
11308         this.snapshot = this.snapshot || this.data;
11309         this.data = this.queryBy(fn, scope||this);
11310         this.fireEvent("datachanged", this);
11311     },
11312
11313     /**
11314      * Query the records by a specified property.
11315      * @param {String} field A field on your records
11316      * @param {String/RegExp} value Either a string that the field
11317      * should start with or a RegExp to test against the field
11318      * @param {Boolean} anyMatch True to match any part not just the beginning
11319      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11320      */
11321     query : function(property, value, anyMatch){
11322         var fn = this.createFilterFn(property, value, anyMatch);
11323         return fn ? this.queryBy(fn) : this.data.clone();
11324     },
11325
11326     /**
11327      * Query by a function. The specified function will be called with each
11328      * record in this data source. If the function returns true the record is included
11329      * in the results.
11330      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11331      * @param {Object} scope (optional) The scope of the function (defaults to this)
11332       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11333      **/
11334     queryBy : function(fn, scope){
11335         var data = this.snapshot || this.data;
11336         return data.filterBy(fn, scope||this);
11337     },
11338
11339     /**
11340      * Collects unique values for a particular dataIndex from this store.
11341      * @param {String} dataIndex The property to collect
11342      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11343      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11344      * @return {Array} An array of the unique values
11345      **/
11346     collect : function(dataIndex, allowNull, bypassFilter){
11347         var d = (bypassFilter === true && this.snapshot) ?
11348                 this.snapshot.items : this.data.items;
11349         var v, sv, r = [], l = {};
11350         for(var i = 0, len = d.length; i < len; i++){
11351             v = d[i].data[dataIndex];
11352             sv = String(v);
11353             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11354                 l[sv] = true;
11355                 r[r.length] = v;
11356             }
11357         }
11358         return r;
11359     },
11360
11361     /**
11362      * Revert to a view of the Record cache with no filtering applied.
11363      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11364      */
11365     clearFilter : function(suppressEvent){
11366         if(this.snapshot && this.snapshot != this.data){
11367             this.data = this.snapshot;
11368             delete this.snapshot;
11369             if(suppressEvent !== true){
11370                 this.fireEvent("datachanged", this);
11371             }
11372         }
11373     },
11374
11375     // private
11376     afterEdit : function(record){
11377         if(this.modified.indexOf(record) == -1){
11378             this.modified.push(record);
11379         }
11380         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11381     },
11382     
11383     // private
11384     afterReject : function(record){
11385         this.modified.remove(record);
11386         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11387     },
11388
11389     // private
11390     afterCommit : function(record){
11391         this.modified.remove(record);
11392         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11393     },
11394
11395     /**
11396      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11397      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11398      */
11399     commitChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].commit();
11404         }
11405     },
11406
11407     /**
11408      * Cancel outstanding changes on all changed records.
11409      */
11410     rejectChanges : function(){
11411         var m = this.modified.slice(0);
11412         this.modified = [];
11413         for(var i = 0, len = m.length; i < len; i++){
11414             m[i].reject();
11415         }
11416     },
11417
11418     onMetaChange : function(meta, rtype, o){
11419         this.recordType = rtype;
11420         this.fields = rtype.prototype.fields;
11421         delete this.snapshot;
11422         this.sortInfo = meta.sortInfo || this.sortInfo;
11423         this.modified = [];
11424         this.fireEvent('metachange', this, this.reader.meta);
11425     },
11426     
11427     moveIndex : function(data, type)
11428     {
11429         var index = this.indexOf(data);
11430         
11431         var newIndex = index + type;
11432         
11433         this.remove(data);
11434         
11435         this.insert(newIndex, data);
11436         
11437     }
11438 });/*
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  * @class Roo.data.SimpleStore
11451  * @extends Roo.data.Store
11452  * Small helper class to make creating Stores from Array data easier.
11453  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11454  * @cfg {Array} fields An array of field definition objects, or field name strings.
11455  * @cfg {Array} data The multi-dimensional array of data
11456  * @constructor
11457  * @param {Object} config
11458  */
11459 Roo.data.SimpleStore = function(config){
11460     Roo.data.SimpleStore.superclass.constructor.call(this, {
11461         isLocal : true,
11462         reader: new Roo.data.ArrayReader({
11463                 id: config.id
11464             },
11465             Roo.data.Record.create(config.fields)
11466         ),
11467         proxy : new Roo.data.MemoryProxy(config.data)
11468     });
11469     this.load();
11470 };
11471 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11472  * Based on:
11473  * Ext JS Library 1.1.1
11474  * Copyright(c) 2006-2007, Ext JS, LLC.
11475  *
11476  * Originally Released Under LGPL - original licence link has changed is not relivant.
11477  *
11478  * Fork - LGPL
11479  * <script type="text/javascript">
11480  */
11481
11482 /**
11483 /**
11484  * @extends Roo.data.Store
11485  * @class Roo.data.JsonStore
11486  * Small helper class to make creating Stores for JSON data easier. <br/>
11487 <pre><code>
11488 var store = new Roo.data.JsonStore({
11489     url: 'get-images.php',
11490     root: 'images',
11491     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11492 });
11493 </code></pre>
11494  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11495  * JsonReader and HttpProxy (unless inline data is provided).</b>
11496  * @cfg {Array} fields An array of field definition objects, or field name strings.
11497  * @constructor
11498  * @param {Object} config
11499  */
11500 Roo.data.JsonStore = function(c){
11501     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11502         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11503         reader: new Roo.data.JsonReader(c, c.fields)
11504     }));
11505 };
11506 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11507  * Based on:
11508  * Ext JS Library 1.1.1
11509  * Copyright(c) 2006-2007, Ext JS, LLC.
11510  *
11511  * Originally Released Under LGPL - original licence link has changed is not relivant.
11512  *
11513  * Fork - LGPL
11514  * <script type="text/javascript">
11515  */
11516
11517  
11518 Roo.data.Field = function(config){
11519     if(typeof config == "string"){
11520         config = {name: config};
11521     }
11522     Roo.apply(this, config);
11523     
11524     if(!this.type){
11525         this.type = "auto";
11526     }
11527     
11528     var st = Roo.data.SortTypes;
11529     // named sortTypes are supported, here we look them up
11530     if(typeof this.sortType == "string"){
11531         this.sortType = st[this.sortType];
11532     }
11533     
11534     // set default sortType for strings and dates
11535     if(!this.sortType){
11536         switch(this.type){
11537             case "string":
11538                 this.sortType = st.asUCString;
11539                 break;
11540             case "date":
11541                 this.sortType = st.asDate;
11542                 break;
11543             default:
11544                 this.sortType = st.none;
11545         }
11546     }
11547
11548     // define once
11549     var stripRe = /[\$,%]/g;
11550
11551     // prebuilt conversion function for this field, instead of
11552     // switching every time we're reading a value
11553     if(!this.convert){
11554         var cv, dateFormat = this.dateFormat;
11555         switch(this.type){
11556             case "":
11557             case "auto":
11558             case undefined:
11559                 cv = function(v){ return v; };
11560                 break;
11561             case "string":
11562                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11563                 break;
11564             case "int":
11565                 cv = function(v){
11566                     return v !== undefined && v !== null && v !== '' ?
11567                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11568                     };
11569                 break;
11570             case "float":
11571                 cv = function(v){
11572                     return v !== undefined && v !== null && v !== '' ?
11573                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11574                     };
11575                 break;
11576             case "bool":
11577             case "boolean":
11578                 cv = function(v){ return v === true || v === "true" || v == 1; };
11579                 break;
11580             case "date":
11581                 cv = function(v){
11582                     if(!v){
11583                         return '';
11584                     }
11585                     if(v instanceof Date){
11586                         return v;
11587                     }
11588                     if(dateFormat){
11589                         if(dateFormat == "timestamp"){
11590                             return new Date(v*1000);
11591                         }
11592                         return Date.parseDate(v, dateFormat);
11593                     }
11594                     var parsed = Date.parse(v);
11595                     return parsed ? new Date(parsed) : null;
11596                 };
11597              break;
11598             
11599         }
11600         this.convert = cv;
11601     }
11602 };
11603
11604 Roo.data.Field.prototype = {
11605     dateFormat: null,
11606     defaultValue: "",
11607     mapping: null,
11608     sortType : null,
11609     sortDir : "ASC"
11610 };/*
11611  * Based on:
11612  * Ext JS Library 1.1.1
11613  * Copyright(c) 2006-2007, Ext JS, LLC.
11614  *
11615  * Originally Released Under LGPL - original licence link has changed is not relivant.
11616  *
11617  * Fork - LGPL
11618  * <script type="text/javascript">
11619  */
11620  
11621 // Base class for reading structured data from a data source.  This class is intended to be
11622 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11623
11624 /**
11625  * @class Roo.data.DataReader
11626  * Base class for reading structured data from a data source.  This class is intended to be
11627  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11628  */
11629
11630 Roo.data.DataReader = function(meta, recordType){
11631     
11632     this.meta = meta;
11633     
11634     this.recordType = recordType instanceof Array ? 
11635         Roo.data.Record.create(recordType) : recordType;
11636 };
11637
11638 Roo.data.DataReader.prototype = {
11639      /**
11640      * Create an empty record
11641      * @param {Object} data (optional) - overlay some values
11642      * @return {Roo.data.Record} record created.
11643      */
11644     newRow :  function(d) {
11645         var da =  {};
11646         this.recordType.prototype.fields.each(function(c) {
11647             switch( c.type) {
11648                 case 'int' : da[c.name] = 0; break;
11649                 case 'date' : da[c.name] = new Date(); break;
11650                 case 'float' : da[c.name] = 0.0; break;
11651                 case 'boolean' : da[c.name] = false; break;
11652                 default : da[c.name] = ""; break;
11653             }
11654             
11655         });
11656         return new this.recordType(Roo.apply(da, d));
11657     }
11658     
11659 };/*
11660  * Based on:
11661  * Ext JS Library 1.1.1
11662  * Copyright(c) 2006-2007, Ext JS, LLC.
11663  *
11664  * Originally Released Under LGPL - original licence link has changed is not relivant.
11665  *
11666  * Fork - LGPL
11667  * <script type="text/javascript">
11668  */
11669
11670 /**
11671  * @class Roo.data.DataProxy
11672  * @extends Roo.data.Observable
11673  * This class is an abstract base class for implementations which provide retrieval of
11674  * unformatted data objects.<br>
11675  * <p>
11676  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11677  * (of the appropriate type which knows how to parse the data object) to provide a block of
11678  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11679  * <p>
11680  * Custom implementations must implement the load method as described in
11681  * {@link Roo.data.HttpProxy#load}.
11682  */
11683 Roo.data.DataProxy = function(){
11684     this.addEvents({
11685         /**
11686          * @event beforeload
11687          * Fires before a network request is made to retrieve a data object.
11688          * @param {Object} This DataProxy object.
11689          * @param {Object} params The params parameter to the load function.
11690          */
11691         beforeload : true,
11692         /**
11693          * @event load
11694          * Fires before the load method's callback is called.
11695          * @param {Object} This DataProxy object.
11696          * @param {Object} o The data object.
11697          * @param {Object} arg The callback argument object passed to the load function.
11698          */
11699         load : true,
11700         /**
11701          * @event loadexception
11702          * Fires if an Exception occurs during data retrieval.
11703          * @param {Object} This DataProxy object.
11704          * @param {Object} o The data object.
11705          * @param {Object} arg The callback argument object passed to the load function.
11706          * @param {Object} e The Exception.
11707          */
11708         loadexception : true
11709     });
11710     Roo.data.DataProxy.superclass.constructor.call(this);
11711 };
11712
11713 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11714
11715     /**
11716      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11717      */
11718 /*
11719  * Based on:
11720  * Ext JS Library 1.1.1
11721  * Copyright(c) 2006-2007, Ext JS, LLC.
11722  *
11723  * Originally Released Under LGPL - original licence link has changed is not relivant.
11724  *
11725  * Fork - LGPL
11726  * <script type="text/javascript">
11727  */
11728 /**
11729  * @class Roo.data.MemoryProxy
11730  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11731  * to the Reader when its load method is called.
11732  * @constructor
11733  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11734  */
11735 Roo.data.MemoryProxy = function(data){
11736     if (data.data) {
11737         data = data.data;
11738     }
11739     Roo.data.MemoryProxy.superclass.constructor.call(this);
11740     this.data = data;
11741 };
11742
11743 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11744     
11745     /**
11746      * Load data from the requested source (in this case an in-memory
11747      * data object passed to the constructor), read the data object into
11748      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11749      * process that block using the passed callback.
11750      * @param {Object} params This parameter is not used by the MemoryProxy class.
11751      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11752      * object into a block of Roo.data.Records.
11753      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11754      * The function must be passed <ul>
11755      * <li>The Record block object</li>
11756      * <li>The "arg" argument from the load function</li>
11757      * <li>A boolean success indicator</li>
11758      * </ul>
11759      * @param {Object} scope The scope in which to call the callback
11760      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11761      */
11762     load : function(params, reader, callback, scope, arg){
11763         params = params || {};
11764         var result;
11765         try {
11766             result = reader.readRecords(this.data);
11767         }catch(e){
11768             this.fireEvent("loadexception", this, arg, null, e);
11769             callback.call(scope, null, arg, false);
11770             return;
11771         }
11772         callback.call(scope, result, arg, true);
11773     },
11774     
11775     // private
11776     update : function(params, records){
11777         
11778     }
11779 });/*
11780  * Based on:
11781  * Ext JS Library 1.1.1
11782  * Copyright(c) 2006-2007, Ext JS, LLC.
11783  *
11784  * Originally Released Under LGPL - original licence link has changed is not relivant.
11785  *
11786  * Fork - LGPL
11787  * <script type="text/javascript">
11788  */
11789 /**
11790  * @class Roo.data.HttpProxy
11791  * @extends Roo.data.DataProxy
11792  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11793  * configured to reference a certain URL.<br><br>
11794  * <p>
11795  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11796  * from which the running page was served.<br><br>
11797  * <p>
11798  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11799  * <p>
11800  * Be aware that to enable the browser to parse an XML document, the server must set
11801  * the Content-Type header in the HTTP response to "text/xml".
11802  * @constructor
11803  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11804  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11805  * will be used to make the request.
11806  */
11807 Roo.data.HttpProxy = function(conn){
11808     Roo.data.HttpProxy.superclass.constructor.call(this);
11809     // is conn a conn config or a real conn?
11810     this.conn = conn;
11811     this.useAjax = !conn || !conn.events;
11812   
11813 };
11814
11815 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11816     // thse are take from connection...
11817     
11818     /**
11819      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11820      */
11821     /**
11822      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11823      * extra parameters to each request made by this object. (defaults to undefined)
11824      */
11825     /**
11826      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11827      *  to each request made by this object. (defaults to undefined)
11828      */
11829     /**
11830      * @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)
11831      */
11832     /**
11833      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11834      */
11835      /**
11836      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11837      * @type Boolean
11838      */
11839   
11840
11841     /**
11842      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11843      * @type Boolean
11844      */
11845     /**
11846      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11847      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11848      * a finer-grained basis than the DataProxy events.
11849      */
11850     getConnection : function(){
11851         return this.useAjax ? Roo.Ajax : this.conn;
11852     },
11853
11854     /**
11855      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11856      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11857      * process that block using the passed callback.
11858      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11859      * for the request to the remote server.
11860      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11861      * object into a block of Roo.data.Records.
11862      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11863      * The function must be passed <ul>
11864      * <li>The Record block object</li>
11865      * <li>The "arg" argument from the load function</li>
11866      * <li>A boolean success indicator</li>
11867      * </ul>
11868      * @param {Object} scope The scope in which to call the callback
11869      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11870      */
11871     load : function(params, reader, callback, scope, arg){
11872         if(this.fireEvent("beforeload", this, params) !== false){
11873             var  o = {
11874                 params : params || {},
11875                 request: {
11876                     callback : callback,
11877                     scope : scope,
11878                     arg : arg
11879                 },
11880                 reader: reader,
11881                 callback : this.loadResponse,
11882                 scope: this
11883             };
11884             if(this.useAjax){
11885                 Roo.applyIf(o, this.conn);
11886                 if(this.activeRequest){
11887                     Roo.Ajax.abort(this.activeRequest);
11888                 }
11889                 this.activeRequest = Roo.Ajax.request(o);
11890             }else{
11891                 this.conn.request(o);
11892             }
11893         }else{
11894             callback.call(scope||this, null, arg, false);
11895         }
11896     },
11897
11898     // private
11899     loadResponse : function(o, success, response){
11900         delete this.activeRequest;
11901         if(!success){
11902             this.fireEvent("loadexception", this, o, response);
11903             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11904             return;
11905         }
11906         var result;
11907         try {
11908             result = o.reader.read(response);
11909         }catch(e){
11910             this.fireEvent("loadexception", this, o, response, e);
11911             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11912             return;
11913         }
11914         
11915         this.fireEvent("load", this, o, o.request.arg);
11916         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11917     },
11918
11919     // private
11920     update : function(dataSet){
11921
11922     },
11923
11924     // private
11925     updateResponse : function(dataSet){
11926
11927     }
11928 });/*
11929  * Based on:
11930  * Ext JS Library 1.1.1
11931  * Copyright(c) 2006-2007, Ext JS, LLC.
11932  *
11933  * Originally Released Under LGPL - original licence link has changed is not relivant.
11934  *
11935  * Fork - LGPL
11936  * <script type="text/javascript">
11937  */
11938
11939 /**
11940  * @class Roo.data.ScriptTagProxy
11941  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11942  * other than the originating domain of the running page.<br><br>
11943  * <p>
11944  * <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
11945  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11946  * <p>
11947  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11948  * source code that is used as the source inside a &lt;script> tag.<br><br>
11949  * <p>
11950  * In order for the browser to process the returned data, the server must wrap the data object
11951  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11952  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11953  * depending on whether the callback name was passed:
11954  * <p>
11955  * <pre><code>
11956 boolean scriptTag = false;
11957 String cb = request.getParameter("callback");
11958 if (cb != null) {
11959     scriptTag = true;
11960     response.setContentType("text/javascript");
11961 } else {
11962     response.setContentType("application/x-json");
11963 }
11964 Writer out = response.getWriter();
11965 if (scriptTag) {
11966     out.write(cb + "(");
11967 }
11968 out.print(dataBlock.toJsonString());
11969 if (scriptTag) {
11970     out.write(");");
11971 }
11972 </pre></code>
11973  *
11974  * @constructor
11975  * @param {Object} config A configuration object.
11976  */
11977 Roo.data.ScriptTagProxy = function(config){
11978     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11979     Roo.apply(this, config);
11980     this.head = document.getElementsByTagName("head")[0];
11981 };
11982
11983 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11984
11985 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11986     /**
11987      * @cfg {String} url The URL from which to request the data object.
11988      */
11989     /**
11990      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11991      */
11992     timeout : 30000,
11993     /**
11994      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11995      * the server the name of the callback function set up by the load call to process the returned data object.
11996      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11997      * javascript output which calls this named function passing the data object as its only parameter.
11998      */
11999     callbackParam : "callback",
12000     /**
12001      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12002      * name to the request.
12003      */
12004     nocache : true,
12005
12006     /**
12007      * Load data from the configured URL, read the data object into
12008      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12009      * process that block using the passed callback.
12010      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12011      * for the request to the remote server.
12012      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12013      * object into a block of Roo.data.Records.
12014      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12015      * The function must be passed <ul>
12016      * <li>The Record block object</li>
12017      * <li>The "arg" argument from the load function</li>
12018      * <li>A boolean success indicator</li>
12019      * </ul>
12020      * @param {Object} scope The scope in which to call the callback
12021      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12022      */
12023     load : function(params, reader, callback, scope, arg){
12024         if(this.fireEvent("beforeload", this, params) !== false){
12025
12026             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12027
12028             var url = this.url;
12029             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12030             if(this.nocache){
12031                 url += "&_dc=" + (new Date().getTime());
12032             }
12033             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12034             var trans = {
12035                 id : transId,
12036                 cb : "stcCallback"+transId,
12037                 scriptId : "stcScript"+transId,
12038                 params : params,
12039                 arg : arg,
12040                 url : url,
12041                 callback : callback,
12042                 scope : scope,
12043                 reader : reader
12044             };
12045             var conn = this;
12046
12047             window[trans.cb] = function(o){
12048                 conn.handleResponse(o, trans);
12049             };
12050
12051             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12052
12053             if(this.autoAbort !== false){
12054                 this.abort();
12055             }
12056
12057             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12058
12059             var script = document.createElement("script");
12060             script.setAttribute("src", url);
12061             script.setAttribute("type", "text/javascript");
12062             script.setAttribute("id", trans.scriptId);
12063             this.head.appendChild(script);
12064
12065             this.trans = trans;
12066         }else{
12067             callback.call(scope||this, null, arg, false);
12068         }
12069     },
12070
12071     // private
12072     isLoading : function(){
12073         return this.trans ? true : false;
12074     },
12075
12076     /**
12077      * Abort the current server request.
12078      */
12079     abort : function(){
12080         if(this.isLoading()){
12081             this.destroyTrans(this.trans);
12082         }
12083     },
12084
12085     // private
12086     destroyTrans : function(trans, isLoaded){
12087         this.head.removeChild(document.getElementById(trans.scriptId));
12088         clearTimeout(trans.timeoutId);
12089         if(isLoaded){
12090             window[trans.cb] = undefined;
12091             try{
12092                 delete window[trans.cb];
12093             }catch(e){}
12094         }else{
12095             // if hasn't been loaded, wait for load to remove it to prevent script error
12096             window[trans.cb] = function(){
12097                 window[trans.cb] = undefined;
12098                 try{
12099                     delete window[trans.cb];
12100                 }catch(e){}
12101             };
12102         }
12103     },
12104
12105     // private
12106     handleResponse : function(o, trans){
12107         this.trans = false;
12108         this.destroyTrans(trans, true);
12109         var result;
12110         try {
12111             result = trans.reader.readRecords(o);
12112         }catch(e){
12113             this.fireEvent("loadexception", this, o, trans.arg, e);
12114             trans.callback.call(trans.scope||window, null, trans.arg, false);
12115             return;
12116         }
12117         this.fireEvent("load", this, o, trans.arg);
12118         trans.callback.call(trans.scope||window, result, trans.arg, true);
12119     },
12120
12121     // private
12122     handleFailure : function(trans){
12123         this.trans = false;
12124         this.destroyTrans(trans, false);
12125         this.fireEvent("loadexception", this, null, trans.arg);
12126         trans.callback.call(trans.scope||window, null, trans.arg, false);
12127     }
12128 });/*
12129  * Based on:
12130  * Ext JS Library 1.1.1
12131  * Copyright(c) 2006-2007, Ext JS, LLC.
12132  *
12133  * Originally Released Under LGPL - original licence link has changed is not relivant.
12134  *
12135  * Fork - LGPL
12136  * <script type="text/javascript">
12137  */
12138
12139 /**
12140  * @class Roo.data.JsonReader
12141  * @extends Roo.data.DataReader
12142  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12143  * based on mappings in a provided Roo.data.Record constructor.
12144  * 
12145  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12146  * in the reply previously. 
12147  * 
12148  * <p>
12149  * Example code:
12150  * <pre><code>
12151 var RecordDef = Roo.data.Record.create([
12152     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12153     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12154 ]);
12155 var myReader = new Roo.data.JsonReader({
12156     totalProperty: "results",    // The property which contains the total dataset size (optional)
12157     root: "rows",                // The property which contains an Array of row objects
12158     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12159 }, RecordDef);
12160 </code></pre>
12161  * <p>
12162  * This would consume a JSON file like this:
12163  * <pre><code>
12164 { 'results': 2, 'rows': [
12165     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12166     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12167 }
12168 </code></pre>
12169  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12170  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12171  * paged from the remote server.
12172  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12173  * @cfg {String} root name of the property which contains the Array of row objects.
12174  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12175  * @cfg {Array} fields Array of field definition objects
12176  * @constructor
12177  * Create a new JsonReader
12178  * @param {Object} meta Metadata configuration options
12179  * @param {Object} recordType Either an Array of field definition objects,
12180  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12181  */
12182 Roo.data.JsonReader = function(meta, recordType){
12183     
12184     meta = meta || {};
12185     // set some defaults:
12186     Roo.applyIf(meta, {
12187         totalProperty: 'total',
12188         successProperty : 'success',
12189         root : 'data',
12190         id : 'id'
12191     });
12192     
12193     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12194 };
12195 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12196     
12197     /**
12198      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12199      * Used by Store query builder to append _requestMeta to params.
12200      * 
12201      */
12202     metaFromRemote : false,
12203     /**
12204      * This method is only used by a DataProxy which has retrieved data from a remote server.
12205      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12206      * @return {Object} data A data block which is used by an Roo.data.Store object as
12207      * a cache of Roo.data.Records.
12208      */
12209     read : function(response){
12210         var json = response.responseText;
12211        
12212         var o = /* eval:var:o */ eval("("+json+")");
12213         if(!o) {
12214             throw {message: "JsonReader.read: Json object not found"};
12215         }
12216         
12217         if(o.metaData){
12218             
12219             delete this.ef;
12220             this.metaFromRemote = true;
12221             this.meta = o.metaData;
12222             this.recordType = Roo.data.Record.create(o.metaData.fields);
12223             this.onMetaChange(this.meta, this.recordType, o);
12224         }
12225         return this.readRecords(o);
12226     },
12227
12228     // private function a store will implement
12229     onMetaChange : function(meta, recordType, o){
12230
12231     },
12232
12233     /**
12234          * @ignore
12235          */
12236     simpleAccess: function(obj, subsc) {
12237         return obj[subsc];
12238     },
12239
12240         /**
12241          * @ignore
12242          */
12243     getJsonAccessor: function(){
12244         var re = /[\[\.]/;
12245         return function(expr) {
12246             try {
12247                 return(re.test(expr))
12248                     ? new Function("obj", "return obj." + expr)
12249                     : function(obj){
12250                         return obj[expr];
12251                     };
12252             } catch(e){}
12253             return Roo.emptyFn;
12254         };
12255     }(),
12256
12257     /**
12258      * Create a data block containing Roo.data.Records from an XML document.
12259      * @param {Object} o An object which contains an Array of row objects in the property specified
12260      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12261      * which contains the total size of the dataset.
12262      * @return {Object} data A data block which is used by an Roo.data.Store object as
12263      * a cache of Roo.data.Records.
12264      */
12265     readRecords : function(o){
12266         /**
12267          * After any data loads, the raw JSON data is available for further custom processing.
12268          * @type Object
12269          */
12270         this.o = o;
12271         var s = this.meta, Record = this.recordType,
12272             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12273
12274 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12275         if (!this.ef) {
12276             if(s.totalProperty) {
12277                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12278                 }
12279                 if(s.successProperty) {
12280                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12281                 }
12282                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12283                 if (s.id) {
12284                         var g = this.getJsonAccessor(s.id);
12285                         this.getId = function(rec) {
12286                                 var r = g(rec);  
12287                                 return (r === undefined || r === "") ? null : r;
12288                         };
12289                 } else {
12290                         this.getId = function(){return null;};
12291                 }
12292             this.ef = [];
12293             for(var jj = 0; jj < fl; jj++){
12294                 f = fi[jj];
12295                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12296                 this.ef[jj] = this.getJsonAccessor(map);
12297             }
12298         }
12299
12300         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12301         if(s.totalProperty){
12302             var vt = parseInt(this.getTotal(o), 10);
12303             if(!isNaN(vt)){
12304                 totalRecords = vt;
12305             }
12306         }
12307         if(s.successProperty){
12308             var vs = this.getSuccess(o);
12309             if(vs === false || vs === 'false'){
12310                 success = false;
12311             }
12312         }
12313         var records = [];
12314         for(var i = 0; i < c; i++){
12315                 var n = root[i];
12316             var values = {};
12317             var id = this.getId(n);
12318             for(var j = 0; j < fl; j++){
12319                 f = fi[j];
12320             var v = this.ef[j](n);
12321             if (!f.convert) {
12322                 Roo.log('missing convert for ' + f.name);
12323                 Roo.log(f);
12324                 continue;
12325             }
12326             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12327             }
12328             var record = new Record(values, id);
12329             record.json = n;
12330             records[i] = record;
12331         }
12332         return {
12333             raw : o,
12334             success : success,
12335             records : records,
12336             totalRecords : totalRecords
12337         };
12338     }
12339 });/*
12340  * Based on:
12341  * Ext JS Library 1.1.1
12342  * Copyright(c) 2006-2007, Ext JS, LLC.
12343  *
12344  * Originally Released Under LGPL - original licence link has changed is not relivant.
12345  *
12346  * Fork - LGPL
12347  * <script type="text/javascript">
12348  */
12349
12350 /**
12351  * @class Roo.data.ArrayReader
12352  * @extends Roo.data.DataReader
12353  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12354  * Each element of that Array represents a row of data fields. The
12355  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12356  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12357  * <p>
12358  * Example code:.
12359  * <pre><code>
12360 var RecordDef = Roo.data.Record.create([
12361     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12362     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12363 ]);
12364 var myReader = new Roo.data.ArrayReader({
12365     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12366 }, RecordDef);
12367 </code></pre>
12368  * <p>
12369  * This would consume an Array like this:
12370  * <pre><code>
12371 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12372   </code></pre>
12373  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12374  * @constructor
12375  * Create a new JsonReader
12376  * @param {Object} meta Metadata configuration options.
12377  * @param {Object} recordType Either an Array of field definition objects
12378  * as specified to {@link Roo.data.Record#create},
12379  * or an {@link Roo.data.Record} object
12380  * created using {@link Roo.data.Record#create}.
12381  */
12382 Roo.data.ArrayReader = function(meta, recordType){
12383     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12384 };
12385
12386 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12387     /**
12388      * Create a data block containing Roo.data.Records from an XML document.
12389      * @param {Object} o An Array of row objects which represents the dataset.
12390      * @return {Object} data A data block which is used by an Roo.data.Store object as
12391      * a cache of Roo.data.Records.
12392      */
12393     readRecords : function(o){
12394         var sid = this.meta ? this.meta.id : null;
12395         var recordType = this.recordType, fields = recordType.prototype.fields;
12396         var records = [];
12397         var root = o;
12398             for(var i = 0; i < root.length; i++){
12399                     var n = root[i];
12400                 var values = {};
12401                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12402                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12403                 var f = fields.items[j];
12404                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12405                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12406                 v = f.convert(v);
12407                 values[f.name] = v;
12408             }
12409                 var record = new recordType(values, id);
12410                 record.json = n;
12411                 records[records.length] = record;
12412             }
12413             return {
12414                 records : records,
12415                 totalRecords : records.length
12416             };
12417     }
12418 });/*
12419  * - LGPL
12420  * * 
12421  */
12422
12423 /**
12424  * @class Roo.bootstrap.ComboBox
12425  * @extends Roo.bootstrap.TriggerField
12426  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12427  * @cfg {Boolean} append (true|false) default false
12428  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12429  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12430  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12431  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12432  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12433  * @cfg {Boolean} animate default true
12434  * @cfg {Boolean} emptyResultText only for touch device
12435  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12436  * @cfg {String} emptyTitle default ''
12437  * @constructor
12438  * Create a new ComboBox.
12439  * @param {Object} config Configuration options
12440  */
12441 Roo.bootstrap.ComboBox = function(config){
12442     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12443     this.addEvents({
12444         /**
12445          * @event expand
12446          * Fires when the dropdown list is expanded
12447         * @param {Roo.bootstrap.ComboBox} combo This combo box
12448         */
12449         'expand' : true,
12450         /**
12451          * @event collapse
12452          * Fires when the dropdown list is collapsed
12453         * @param {Roo.bootstrap.ComboBox} combo This combo box
12454         */
12455         'collapse' : true,
12456         /**
12457          * @event beforeselect
12458          * Fires before a list item is selected. Return false to cancel the selection.
12459         * @param {Roo.bootstrap.ComboBox} combo This combo box
12460         * @param {Roo.data.Record} record The data record returned from the underlying store
12461         * @param {Number} index The index of the selected item in the dropdown list
12462         */
12463         'beforeselect' : true,
12464         /**
12465          * @event select
12466          * Fires when a list item is selected
12467         * @param {Roo.bootstrap.ComboBox} combo This combo box
12468         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12469         * @param {Number} index The index of the selected item in the dropdown list
12470         */
12471         'select' : true,
12472         /**
12473          * @event beforequery
12474          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12475          * The event object passed has these properties:
12476         * @param {Roo.bootstrap.ComboBox} combo This combo box
12477         * @param {String} query The query
12478         * @param {Boolean} forceAll true to force "all" query
12479         * @param {Boolean} cancel true to cancel the query
12480         * @param {Object} e The query event object
12481         */
12482         'beforequery': true,
12483          /**
12484          * @event add
12485          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12486         * @param {Roo.bootstrap.ComboBox} combo This combo box
12487         */
12488         'add' : true,
12489         /**
12490          * @event edit
12491          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12492         * @param {Roo.bootstrap.ComboBox} combo This combo box
12493         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12494         */
12495         'edit' : true,
12496         /**
12497          * @event remove
12498          * Fires when the remove value from the combobox array
12499         * @param {Roo.bootstrap.ComboBox} combo This combo box
12500         */
12501         'remove' : true,
12502         /**
12503          * @event afterremove
12504          * Fires when the remove value from the combobox array
12505         * @param {Roo.bootstrap.ComboBox} combo This combo box
12506         */
12507         'afterremove' : true,
12508         /**
12509          * @event specialfilter
12510          * Fires when specialfilter
12511             * @param {Roo.bootstrap.ComboBox} combo This combo box
12512             */
12513         'specialfilter' : true,
12514         /**
12515          * @event tick
12516          * Fires when tick the element
12517             * @param {Roo.bootstrap.ComboBox} combo This combo box
12518             */
12519         'tick' : true,
12520         /**
12521          * @event touchviewdisplay
12522          * Fires when touch view require special display (default is using displayField)
12523             * @param {Roo.bootstrap.ComboBox} combo This combo box
12524             * @param {Object} cfg set html .
12525             */
12526         'touchviewdisplay' : true
12527         
12528     });
12529     
12530     this.item = [];
12531     this.tickItems = [];
12532     
12533     this.selectedIndex = -1;
12534     if(this.mode == 'local'){
12535         if(config.queryDelay === undefined){
12536             this.queryDelay = 10;
12537         }
12538         if(config.minChars === undefined){
12539             this.minChars = 0;
12540         }
12541     }
12542 };
12543
12544 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12545      
12546     /**
12547      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12548      * rendering into an Roo.Editor, defaults to false)
12549      */
12550     /**
12551      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12552      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12553      */
12554     /**
12555      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12556      */
12557     /**
12558      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12559      * the dropdown list (defaults to undefined, with no header element)
12560      */
12561
12562      /**
12563      * @cfg {String/Roo.Template} tpl The template to use to render the output
12564      */
12565      
12566      /**
12567      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12568      */
12569     listWidth: undefined,
12570     /**
12571      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12572      * mode = 'remote' or 'text' if mode = 'local')
12573      */
12574     displayField: undefined,
12575     
12576     /**
12577      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12578      * mode = 'remote' or 'value' if mode = 'local'). 
12579      * Note: use of a valueField requires the user make a selection
12580      * in order for a value to be mapped.
12581      */
12582     valueField: undefined,
12583     /**
12584      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12585      */
12586     modalTitle : '',
12587     
12588     /**
12589      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12590      * field's data value (defaults to the underlying DOM element's name)
12591      */
12592     hiddenName: undefined,
12593     /**
12594      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12595      */
12596     listClass: '',
12597     /**
12598      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12599      */
12600     selectedClass: 'active',
12601     
12602     /**
12603      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12604      */
12605     shadow:'sides',
12606     /**
12607      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12608      * anchor positions (defaults to 'tl-bl')
12609      */
12610     listAlign: 'tl-bl?',
12611     /**
12612      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12613      */
12614     maxHeight: 300,
12615     /**
12616      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12617      * query specified by the allQuery config option (defaults to 'query')
12618      */
12619     triggerAction: 'query',
12620     /**
12621      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12622      * (defaults to 4, does not apply if editable = false)
12623      */
12624     minChars : 4,
12625     /**
12626      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12627      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12628      */
12629     typeAhead: false,
12630     /**
12631      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12632      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12633      */
12634     queryDelay: 500,
12635     /**
12636      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12637      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12638      */
12639     pageSize: 0,
12640     /**
12641      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12642      * when editable = true (defaults to false)
12643      */
12644     selectOnFocus:false,
12645     /**
12646      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12647      */
12648     queryParam: 'query',
12649     /**
12650      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12651      * when mode = 'remote' (defaults to 'Loading...')
12652      */
12653     loadingText: 'Loading...',
12654     /**
12655      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12656      */
12657     resizable: false,
12658     /**
12659      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12660      */
12661     handleHeight : 8,
12662     /**
12663      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12664      * traditional select (defaults to true)
12665      */
12666     editable: true,
12667     /**
12668      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12669      */
12670     allQuery: '',
12671     /**
12672      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12673      */
12674     mode: 'remote',
12675     /**
12676      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12677      * listWidth has a higher value)
12678      */
12679     minListWidth : 70,
12680     /**
12681      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12682      * allow the user to set arbitrary text into the field (defaults to false)
12683      */
12684     forceSelection:false,
12685     /**
12686      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12687      * if typeAhead = true (defaults to 250)
12688      */
12689     typeAheadDelay : 250,
12690     /**
12691      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12692      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12693      */
12694     valueNotFoundText : undefined,
12695     /**
12696      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12697      */
12698     blockFocus : false,
12699     
12700     /**
12701      * @cfg {Boolean} disableClear Disable showing of clear button.
12702      */
12703     disableClear : false,
12704     /**
12705      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12706      */
12707     alwaysQuery : false,
12708     
12709     /**
12710      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12711      */
12712     multiple : false,
12713     
12714     /**
12715      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12716      */
12717     invalidClass : "has-warning",
12718     
12719     /**
12720      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12721      */
12722     validClass : "has-success",
12723     
12724     /**
12725      * @cfg {Boolean} specialFilter (true|false) special filter default false
12726      */
12727     specialFilter : false,
12728     
12729     /**
12730      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12731      */
12732     mobileTouchView : true,
12733     
12734     /**
12735      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12736      */
12737     useNativeIOS : false,
12738     
12739     ios_options : false,
12740     
12741     //private
12742     addicon : false,
12743     editicon: false,
12744     
12745     page: 0,
12746     hasQuery: false,
12747     append: false,
12748     loadNext: false,
12749     autoFocus : true,
12750     tickable : false,
12751     btnPosition : 'right',
12752     triggerList : true,
12753     showToggleBtn : true,
12754     animate : true,
12755     emptyResultText: 'Empty',
12756     triggerText : 'Select',
12757     emptyTitle : '',
12758     
12759     // element that contains real text value.. (when hidden is used..)
12760     
12761     getAutoCreate : function()
12762     {   
12763         var cfg = false;
12764         //render
12765         /*
12766          * Render classic select for iso
12767          */
12768         
12769         if(Roo.isIOS && this.useNativeIOS){
12770             cfg = this.getAutoCreateNativeIOS();
12771             return cfg;
12772         }
12773         
12774         /*
12775          * Touch Devices
12776          */
12777         
12778         if(Roo.isTouch && this.mobileTouchView){
12779             cfg = this.getAutoCreateTouchView();
12780             return cfg;;
12781         }
12782         
12783         /*
12784          *  Normal ComboBox
12785          */
12786         if(!this.tickable){
12787             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12788             return cfg;
12789         }
12790         
12791         /*
12792          *  ComboBox with tickable selections
12793          */
12794              
12795         var align = this.labelAlign || this.parentLabelAlign();
12796         
12797         cfg = {
12798             cls : 'form-group roo-combobox-tickable' //input-group
12799         };
12800         
12801         var btn_text_select = '';
12802         var btn_text_done = '';
12803         var btn_text_cancel = '';
12804         
12805         if (this.btn_text_show) {
12806             btn_text_select = 'Select';
12807             btn_text_done = 'Done';
12808             btn_text_cancel = 'Cancel'; 
12809         }
12810         
12811         var buttons = {
12812             tag : 'div',
12813             cls : 'tickable-buttons',
12814             cn : [
12815                 {
12816                     tag : 'button',
12817                     type : 'button',
12818                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12819                     //html : this.triggerText
12820                     html: btn_text_select
12821                 },
12822                 {
12823                     tag : 'button',
12824                     type : 'button',
12825                     name : 'ok',
12826                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12827                     //html : 'Done'
12828                     html: btn_text_done
12829                 },
12830                 {
12831                     tag : 'button',
12832                     type : 'button',
12833                     name : 'cancel',
12834                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12835                     //html : 'Cancel'
12836                     html: btn_text_cancel
12837                 }
12838             ]
12839         };
12840         
12841         if(this.editable){
12842             buttons.cn.unshift({
12843                 tag: 'input',
12844                 cls: 'roo-select2-search-field-input'
12845             });
12846         }
12847         
12848         var _this = this;
12849         
12850         Roo.each(buttons.cn, function(c){
12851             if (_this.size) {
12852                 c.cls += ' btn-' + _this.size;
12853             }
12854
12855             if (_this.disabled) {
12856                 c.disabled = true;
12857             }
12858         });
12859         
12860         var box = {
12861             tag: 'div',
12862             cn: [
12863                 {
12864                     tag: 'input',
12865                     type : 'hidden',
12866                     cls: 'form-hidden-field'
12867                 },
12868                 {
12869                     tag: 'ul',
12870                     cls: 'roo-select2-choices',
12871                     cn:[
12872                         {
12873                             tag: 'li',
12874                             cls: 'roo-select2-search-field',
12875                             cn: [
12876                                 buttons
12877                             ]
12878                         }
12879                     ]
12880                 }
12881             ]
12882         };
12883         
12884         var combobox = {
12885             cls: 'roo-select2-container input-group roo-select2-container-multi',
12886             cn: [
12887                 box
12888 //                {
12889 //                    tag: 'ul',
12890 //                    cls: 'typeahead typeahead-long dropdown-menu',
12891 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12892 //                }
12893             ]
12894         };
12895         
12896         if(this.hasFeedback && !this.allowBlank){
12897             
12898             var feedback = {
12899                 tag: 'span',
12900                 cls: 'glyphicon form-control-feedback'
12901             };
12902
12903             combobox.cn.push(feedback);
12904         }
12905         
12906         
12907         if (align ==='left' && this.fieldLabel.length) {
12908             
12909             cfg.cls += ' roo-form-group-label-left';
12910             
12911             cfg.cn = [
12912                 {
12913                     tag : 'i',
12914                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12915                     tooltip : 'This field is required'
12916                 },
12917                 {
12918                     tag: 'label',
12919                     'for' :  id,
12920                     cls : 'control-label',
12921                     html : this.fieldLabel
12922
12923                 },
12924                 {
12925                     cls : "", 
12926                     cn: [
12927                         combobox
12928                     ]
12929                 }
12930
12931             ];
12932             
12933             var labelCfg = cfg.cn[1];
12934             var contentCfg = cfg.cn[2];
12935             
12936
12937             if(this.indicatorpos == 'right'){
12938                 
12939                 cfg.cn = [
12940                     {
12941                         tag: 'label',
12942                         'for' :  id,
12943                         cls : 'control-label',
12944                         cn : [
12945                             {
12946                                 tag : 'span',
12947                                 html : this.fieldLabel
12948                             },
12949                             {
12950                                 tag : 'i',
12951                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12952                                 tooltip : 'This field is required'
12953                             }
12954                         ]
12955                     },
12956                     {
12957                         cls : "",
12958                         cn: [
12959                             combobox
12960                         ]
12961                     }
12962
12963                 ];
12964                 
12965                 
12966                 
12967                 labelCfg = cfg.cn[0];
12968                 contentCfg = cfg.cn[1];
12969             
12970             }
12971             
12972             if(this.labelWidth > 12){
12973                 labelCfg.style = "width: " + this.labelWidth + 'px';
12974             }
12975             
12976             if(this.labelWidth < 13 && this.labelmd == 0){
12977                 this.labelmd = this.labelWidth;
12978             }
12979             
12980             if(this.labellg > 0){
12981                 labelCfg.cls += ' col-lg-' + this.labellg;
12982                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12983             }
12984             
12985             if(this.labelmd > 0){
12986                 labelCfg.cls += ' col-md-' + this.labelmd;
12987                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12988             }
12989             
12990             if(this.labelsm > 0){
12991                 labelCfg.cls += ' col-sm-' + this.labelsm;
12992                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12993             }
12994             
12995             if(this.labelxs > 0){
12996                 labelCfg.cls += ' col-xs-' + this.labelxs;
12997                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12998             }
12999                 
13000                 
13001         } else if ( this.fieldLabel.length) {
13002 //                Roo.log(" label");
13003                  cfg.cn = [
13004                     {
13005                         tag : 'i',
13006                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13007                         tooltip : 'This field is required'
13008                     },
13009                     {
13010                         tag: 'label',
13011                         //cls : 'input-group-addon',
13012                         html : this.fieldLabel
13013                     },
13014                     combobox
13015                 ];
13016                 
13017                 if(this.indicatorpos == 'right'){
13018                     cfg.cn = [
13019                         {
13020                             tag: 'label',
13021                             //cls : 'input-group-addon',
13022                             html : this.fieldLabel
13023                         },
13024                         {
13025                             tag : 'i',
13026                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13027                             tooltip : 'This field is required'
13028                         },
13029                         combobox
13030                     ];
13031                     
13032                 }
13033
13034         } else {
13035             
13036 //                Roo.log(" no label && no align");
13037                 cfg = combobox
13038                      
13039                 
13040         }
13041          
13042         var settings=this;
13043         ['xs','sm','md','lg'].map(function(size){
13044             if (settings[size]) {
13045                 cfg.cls += ' col-' + size + '-' + settings[size];
13046             }
13047         });
13048         
13049         return cfg;
13050         
13051     },
13052     
13053     _initEventsCalled : false,
13054     
13055     // private
13056     initEvents: function()
13057     {   
13058         if (this._initEventsCalled) { // as we call render... prevent looping...
13059             return;
13060         }
13061         this._initEventsCalled = true;
13062         
13063         if (!this.store) {
13064             throw "can not find store for combo";
13065         }
13066         
13067         this.indicator = this.indicatorEl();
13068         
13069         this.store = Roo.factory(this.store, Roo.data);
13070         this.store.parent = this;
13071         
13072         // if we are building from html. then this element is so complex, that we can not really
13073         // use the rendered HTML.
13074         // so we have to trash and replace the previous code.
13075         if (Roo.XComponent.build_from_html) {
13076             // remove this element....
13077             var e = this.el.dom, k=0;
13078             while (e ) { e = e.previousSibling;  ++k;}
13079
13080             this.el.remove();
13081             
13082             this.el=false;
13083             this.rendered = false;
13084             
13085             this.render(this.parent().getChildContainer(true), k);
13086         }
13087         
13088         if(Roo.isIOS && this.useNativeIOS){
13089             this.initIOSView();
13090             return;
13091         }
13092         
13093         /*
13094          * Touch Devices
13095          */
13096         
13097         if(Roo.isTouch && this.mobileTouchView){
13098             this.initTouchView();
13099             return;
13100         }
13101         
13102         if(this.tickable){
13103             this.initTickableEvents();
13104             return;
13105         }
13106         
13107         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13108         
13109         if(this.hiddenName){
13110             
13111             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13112             
13113             this.hiddenField.dom.value =
13114                 this.hiddenValue !== undefined ? this.hiddenValue :
13115                 this.value !== undefined ? this.value : '';
13116
13117             // prevent input submission
13118             this.el.dom.removeAttribute('name');
13119             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13120              
13121              
13122         }
13123         //if(Roo.isGecko){
13124         //    this.el.dom.setAttribute('autocomplete', 'off');
13125         //}
13126         
13127         var cls = 'x-combo-list';
13128         
13129         //this.list = new Roo.Layer({
13130         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13131         //});
13132         
13133         var _this = this;
13134         
13135         (function(){
13136             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13137             _this.list.setWidth(lw);
13138         }).defer(100);
13139         
13140         this.list.on('mouseover', this.onViewOver, this);
13141         this.list.on('mousemove', this.onViewMove, this);
13142         this.list.on('scroll', this.onViewScroll, this);
13143         
13144         /*
13145         this.list.swallowEvent('mousewheel');
13146         this.assetHeight = 0;
13147
13148         if(this.title){
13149             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13150             this.assetHeight += this.header.getHeight();
13151         }
13152
13153         this.innerList = this.list.createChild({cls:cls+'-inner'});
13154         this.innerList.on('mouseover', this.onViewOver, this);
13155         this.innerList.on('mousemove', this.onViewMove, this);
13156         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13157         
13158         if(this.allowBlank && !this.pageSize && !this.disableClear){
13159             this.footer = this.list.createChild({cls:cls+'-ft'});
13160             this.pageTb = new Roo.Toolbar(this.footer);
13161            
13162         }
13163         if(this.pageSize){
13164             this.footer = this.list.createChild({cls:cls+'-ft'});
13165             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13166                     {pageSize: this.pageSize});
13167             
13168         }
13169         
13170         if (this.pageTb && this.allowBlank && !this.disableClear) {
13171             var _this = this;
13172             this.pageTb.add(new Roo.Toolbar.Fill(), {
13173                 cls: 'x-btn-icon x-btn-clear',
13174                 text: '&#160;',
13175                 handler: function()
13176                 {
13177                     _this.collapse();
13178                     _this.clearValue();
13179                     _this.onSelect(false, -1);
13180                 }
13181             });
13182         }
13183         if (this.footer) {
13184             this.assetHeight += this.footer.getHeight();
13185         }
13186         */
13187             
13188         if(!this.tpl){
13189             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13190         }
13191
13192         this.view = new Roo.View(this.list, this.tpl, {
13193             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13194         });
13195         //this.view.wrapEl.setDisplayed(false);
13196         this.view.on('click', this.onViewClick, this);
13197         
13198         
13199         this.store.on('beforeload', this.onBeforeLoad, this);
13200         this.store.on('load', this.onLoad, this);
13201         this.store.on('loadexception', this.onLoadException, this);
13202         /*
13203         if(this.resizable){
13204             this.resizer = new Roo.Resizable(this.list,  {
13205                pinned:true, handles:'se'
13206             });
13207             this.resizer.on('resize', function(r, w, h){
13208                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13209                 this.listWidth = w;
13210                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13211                 this.restrictHeight();
13212             }, this);
13213             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13214         }
13215         */
13216         if(!this.editable){
13217             this.editable = true;
13218             this.setEditable(false);
13219         }
13220         
13221         /*
13222         
13223         if (typeof(this.events.add.listeners) != 'undefined') {
13224             
13225             this.addicon = this.wrap.createChild(
13226                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13227        
13228             this.addicon.on('click', function(e) {
13229                 this.fireEvent('add', this);
13230             }, this);
13231         }
13232         if (typeof(this.events.edit.listeners) != 'undefined') {
13233             
13234             this.editicon = this.wrap.createChild(
13235                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13236             if (this.addicon) {
13237                 this.editicon.setStyle('margin-left', '40px');
13238             }
13239             this.editicon.on('click', function(e) {
13240                 
13241                 // we fire even  if inothing is selected..
13242                 this.fireEvent('edit', this, this.lastData );
13243                 
13244             }, this);
13245         }
13246         */
13247         
13248         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13249             "up" : function(e){
13250                 this.inKeyMode = true;
13251                 this.selectPrev();
13252             },
13253
13254             "down" : function(e){
13255                 if(!this.isExpanded()){
13256                     this.onTriggerClick();
13257                 }else{
13258                     this.inKeyMode = true;
13259                     this.selectNext();
13260                 }
13261             },
13262
13263             "enter" : function(e){
13264 //                this.onViewClick();
13265                 //return true;
13266                 this.collapse();
13267                 
13268                 if(this.fireEvent("specialkey", this, e)){
13269                     this.onViewClick(false);
13270                 }
13271                 
13272                 return true;
13273             },
13274
13275             "esc" : function(e){
13276                 this.collapse();
13277             },
13278
13279             "tab" : function(e){
13280                 this.collapse();
13281                 
13282                 if(this.fireEvent("specialkey", this, e)){
13283                     this.onViewClick(false);
13284                 }
13285                 
13286                 return true;
13287             },
13288
13289             scope : this,
13290
13291             doRelay : function(foo, bar, hname){
13292                 if(hname == 'down' || this.scope.isExpanded()){
13293                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13294                 }
13295                 return true;
13296             },
13297
13298             forceKeyDown: true
13299         });
13300         
13301         
13302         this.queryDelay = Math.max(this.queryDelay || 10,
13303                 this.mode == 'local' ? 10 : 250);
13304         
13305         
13306         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13307         
13308         if(this.typeAhead){
13309             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13310         }
13311         if(this.editable !== false){
13312             this.inputEl().on("keyup", this.onKeyUp, this);
13313         }
13314         if(this.forceSelection){
13315             this.inputEl().on('blur', this.doForce, this);
13316         }
13317         
13318         if(this.multiple){
13319             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13320             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13321         }
13322     },
13323     
13324     initTickableEvents: function()
13325     {   
13326         this.createList();
13327         
13328         if(this.hiddenName){
13329             
13330             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13331             
13332             this.hiddenField.dom.value =
13333                 this.hiddenValue !== undefined ? this.hiddenValue :
13334                 this.value !== undefined ? this.value : '';
13335
13336             // prevent input submission
13337             this.el.dom.removeAttribute('name');
13338             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13339              
13340              
13341         }
13342         
13343 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13344         
13345         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13346         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13347         if(this.triggerList){
13348             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13349         }
13350          
13351         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13352         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13353         
13354         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13355         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13356         
13357         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13358         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13359         
13360         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13361         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13362         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13363         
13364         this.okBtn.hide();
13365         this.cancelBtn.hide();
13366         
13367         var _this = this;
13368         
13369         (function(){
13370             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13371             _this.list.setWidth(lw);
13372         }).defer(100);
13373         
13374         this.list.on('mouseover', this.onViewOver, this);
13375         this.list.on('mousemove', this.onViewMove, this);
13376         
13377         this.list.on('scroll', this.onViewScroll, this);
13378         
13379         if(!this.tpl){
13380             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>';
13381         }
13382
13383         this.view = new Roo.View(this.list, this.tpl, {
13384             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13385         });
13386         
13387         //this.view.wrapEl.setDisplayed(false);
13388         this.view.on('click', this.onViewClick, this);
13389         
13390         
13391         
13392         this.store.on('beforeload', this.onBeforeLoad, this);
13393         this.store.on('load', this.onLoad, this);
13394         this.store.on('loadexception', this.onLoadException, this);
13395         
13396         if(this.editable){
13397             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13398                 "up" : function(e){
13399                     this.inKeyMode = true;
13400                     this.selectPrev();
13401                 },
13402
13403                 "down" : function(e){
13404                     this.inKeyMode = true;
13405                     this.selectNext();
13406                 },
13407
13408                 "enter" : function(e){
13409                     if(this.fireEvent("specialkey", this, e)){
13410                         this.onViewClick(false);
13411                     }
13412                     
13413                     return true;
13414                 },
13415
13416                 "esc" : function(e){
13417                     this.onTickableFooterButtonClick(e, false, false);
13418                 },
13419
13420                 "tab" : function(e){
13421                     this.fireEvent("specialkey", this, e);
13422                     
13423                     this.onTickableFooterButtonClick(e, false, false);
13424                     
13425                     return true;
13426                 },
13427
13428                 scope : this,
13429
13430                 doRelay : function(e, fn, key){
13431                     if(this.scope.isExpanded()){
13432                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13433                     }
13434                     return true;
13435                 },
13436
13437                 forceKeyDown: true
13438             });
13439         }
13440         
13441         this.queryDelay = Math.max(this.queryDelay || 10,
13442                 this.mode == 'local' ? 10 : 250);
13443         
13444         
13445         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13446         
13447         if(this.typeAhead){
13448             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13449         }
13450         
13451         if(this.editable !== false){
13452             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13453         }
13454         
13455         this.indicator = this.indicatorEl();
13456         
13457         if(this.indicator){
13458             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13459             this.indicator.hide();
13460         }
13461         
13462     },
13463
13464     onDestroy : function(){
13465         if(this.view){
13466             this.view.setStore(null);
13467             this.view.el.removeAllListeners();
13468             this.view.el.remove();
13469             this.view.purgeListeners();
13470         }
13471         if(this.list){
13472             this.list.dom.innerHTML  = '';
13473         }
13474         
13475         if(this.store){
13476             this.store.un('beforeload', this.onBeforeLoad, this);
13477             this.store.un('load', this.onLoad, this);
13478             this.store.un('loadexception', this.onLoadException, this);
13479         }
13480         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13481     },
13482
13483     // private
13484     fireKey : function(e){
13485         if(e.isNavKeyPress() && !this.list.isVisible()){
13486             this.fireEvent("specialkey", this, e);
13487         }
13488     },
13489
13490     // private
13491     onResize: function(w, h){
13492 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13493 //        
13494 //        if(typeof w != 'number'){
13495 //            // we do not handle it!?!?
13496 //            return;
13497 //        }
13498 //        var tw = this.trigger.getWidth();
13499 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13500 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13501 //        var x = w - tw;
13502 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13503 //            
13504 //        //this.trigger.setStyle('left', x+'px');
13505 //        
13506 //        if(this.list && this.listWidth === undefined){
13507 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13508 //            this.list.setWidth(lw);
13509 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13510 //        }
13511         
13512     
13513         
13514     },
13515
13516     /**
13517      * Allow or prevent the user from directly editing the field text.  If false is passed,
13518      * the user will only be able to select from the items defined in the dropdown list.  This method
13519      * is the runtime equivalent of setting the 'editable' config option at config time.
13520      * @param {Boolean} value True to allow the user to directly edit the field text
13521      */
13522     setEditable : function(value){
13523         if(value == this.editable){
13524             return;
13525         }
13526         this.editable = value;
13527         if(!value){
13528             this.inputEl().dom.setAttribute('readOnly', true);
13529             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13530             this.inputEl().addClass('x-combo-noedit');
13531         }else{
13532             this.inputEl().dom.setAttribute('readOnly', false);
13533             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13534             this.inputEl().removeClass('x-combo-noedit');
13535         }
13536     },
13537
13538     // private
13539     
13540     onBeforeLoad : function(combo,opts){
13541         if(!this.hasFocus){
13542             return;
13543         }
13544          if (!opts.add) {
13545             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13546          }
13547         this.restrictHeight();
13548         this.selectedIndex = -1;
13549     },
13550
13551     // private
13552     onLoad : function(){
13553         
13554         this.hasQuery = false;
13555         
13556         if(!this.hasFocus){
13557             return;
13558         }
13559         
13560         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13561             this.loading.hide();
13562         }
13563         
13564         if(this.store.getCount() > 0){
13565             
13566             this.expand();
13567             this.restrictHeight();
13568             if(this.lastQuery == this.allQuery){
13569                 if(this.editable && !this.tickable){
13570                     this.inputEl().dom.select();
13571                 }
13572                 
13573                 if(
13574                     !this.selectByValue(this.value, true) &&
13575                     this.autoFocus && 
13576                     (
13577                         !this.store.lastOptions ||
13578                         typeof(this.store.lastOptions.add) == 'undefined' || 
13579                         this.store.lastOptions.add != true
13580                     )
13581                 ){
13582                     this.select(0, true);
13583                 }
13584             }else{
13585                 if(this.autoFocus){
13586                     this.selectNext();
13587                 }
13588                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13589                     this.taTask.delay(this.typeAheadDelay);
13590                 }
13591             }
13592         }else{
13593             this.onEmptyResults();
13594         }
13595         
13596         //this.el.focus();
13597     },
13598     // private
13599     onLoadException : function()
13600     {
13601         this.hasQuery = false;
13602         
13603         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13604             this.loading.hide();
13605         }
13606         
13607         if(this.tickable && this.editable){
13608             return;
13609         }
13610         
13611         this.collapse();
13612         // only causes errors at present
13613         //Roo.log(this.store.reader.jsonData);
13614         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13615             // fixme
13616             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13617         //}
13618         
13619         
13620     },
13621     // private
13622     onTypeAhead : function(){
13623         if(this.store.getCount() > 0){
13624             var r = this.store.getAt(0);
13625             var newValue = r.data[this.displayField];
13626             var len = newValue.length;
13627             var selStart = this.getRawValue().length;
13628             
13629             if(selStart != len){
13630                 this.setRawValue(newValue);
13631                 this.selectText(selStart, newValue.length);
13632             }
13633         }
13634     },
13635
13636     // private
13637     onSelect : function(record, index){
13638         
13639         if(this.fireEvent('beforeselect', this, record, index) !== false){
13640         
13641             this.setFromData(index > -1 ? record.data : false);
13642             
13643             this.collapse();
13644             this.fireEvent('select', this, record, index);
13645         }
13646     },
13647
13648     /**
13649      * Returns the currently selected field value or empty string if no value is set.
13650      * @return {String} value The selected value
13651      */
13652     getValue : function()
13653     {
13654         if(Roo.isIOS && this.useNativeIOS){
13655             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13656         }
13657         
13658         if(this.multiple){
13659             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13660         }
13661         
13662         if(this.valueField){
13663             return typeof this.value != 'undefined' ? this.value : '';
13664         }else{
13665             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13666         }
13667     },
13668     
13669     getRawValue : function()
13670     {
13671         if(Roo.isIOS && this.useNativeIOS){
13672             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13673         }
13674         
13675         var v = this.inputEl().getValue();
13676         
13677         return v;
13678     },
13679
13680     /**
13681      * Clears any text/value currently set in the field
13682      */
13683     clearValue : function(){
13684         
13685         if(this.hiddenField){
13686             this.hiddenField.dom.value = '';
13687         }
13688         this.value = '';
13689         this.setRawValue('');
13690         this.lastSelectionText = '';
13691         this.lastData = false;
13692         
13693         var close = this.closeTriggerEl();
13694         
13695         if(close){
13696             close.hide();
13697         }
13698         
13699         this.validate();
13700         
13701     },
13702
13703     /**
13704      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13705      * will be displayed in the field.  If the value does not match the data value of an existing item,
13706      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13707      * Otherwise the field will be blank (although the value will still be set).
13708      * @param {String} value The value to match
13709      */
13710     setValue : function(v)
13711     {
13712         if(Roo.isIOS && this.useNativeIOS){
13713             this.setIOSValue(v);
13714             return;
13715         }
13716         
13717         if(this.multiple){
13718             this.syncValue();
13719             return;
13720         }
13721         
13722         var text = v;
13723         if(this.valueField){
13724             var r = this.findRecord(this.valueField, v);
13725             if(r){
13726                 text = r.data[this.displayField];
13727             }else if(this.valueNotFoundText !== undefined){
13728                 text = this.valueNotFoundText;
13729             }
13730         }
13731         this.lastSelectionText = text;
13732         if(this.hiddenField){
13733             this.hiddenField.dom.value = v;
13734         }
13735         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13736         this.value = v;
13737         
13738         var close = this.closeTriggerEl();
13739         
13740         if(close){
13741             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13742         }
13743         
13744         this.validate();
13745     },
13746     /**
13747      * @property {Object} the last set data for the element
13748      */
13749     
13750     lastData : false,
13751     /**
13752      * Sets the value of the field based on a object which is related to the record format for the store.
13753      * @param {Object} value the value to set as. or false on reset?
13754      */
13755     setFromData : function(o){
13756         
13757         if(this.multiple){
13758             this.addItem(o);
13759             return;
13760         }
13761             
13762         var dv = ''; // display value
13763         var vv = ''; // value value..
13764         this.lastData = o;
13765         if (this.displayField) {
13766             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13767         } else {
13768             // this is an error condition!!!
13769             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13770         }
13771         
13772         if(this.valueField){
13773             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13774         }
13775         
13776         var close = this.closeTriggerEl();
13777         
13778         if(close){
13779             if(dv.length || vv * 1 > 0){
13780                 close.show() ;
13781                 this.blockFocus=true;
13782             } else {
13783                 close.hide();
13784             }             
13785         }
13786         
13787         if(this.hiddenField){
13788             this.hiddenField.dom.value = vv;
13789             
13790             this.lastSelectionText = dv;
13791             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13792             this.value = vv;
13793             return;
13794         }
13795         // no hidden field.. - we store the value in 'value', but still display
13796         // display field!!!!
13797         this.lastSelectionText = dv;
13798         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13799         this.value = vv;
13800         
13801         
13802         
13803     },
13804     // private
13805     reset : function(){
13806         // overridden so that last data is reset..
13807         
13808         if(this.multiple){
13809             this.clearItem();
13810             return;
13811         }
13812         
13813         this.setValue(this.originalValue);
13814         //this.clearInvalid();
13815         this.lastData = false;
13816         if (this.view) {
13817             this.view.clearSelections();
13818         }
13819         
13820         this.validate();
13821     },
13822     // private
13823     findRecord : function(prop, value){
13824         var record;
13825         if(this.store.getCount() > 0){
13826             this.store.each(function(r){
13827                 if(r.data[prop] == value){
13828                     record = r;
13829                     return false;
13830                 }
13831                 return true;
13832             });
13833         }
13834         return record;
13835     },
13836     
13837     getName: function()
13838     {
13839         // returns hidden if it's set..
13840         if (!this.rendered) {return ''};
13841         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13842         
13843     },
13844     // private
13845     onViewMove : function(e, t){
13846         this.inKeyMode = false;
13847     },
13848
13849     // private
13850     onViewOver : function(e, t){
13851         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13852             return;
13853         }
13854         var item = this.view.findItemFromChild(t);
13855         
13856         if(item){
13857             var index = this.view.indexOf(item);
13858             this.select(index, false);
13859         }
13860     },
13861
13862     // private
13863     onViewClick : function(view, doFocus, el, e)
13864     {
13865         var index = this.view.getSelectedIndexes()[0];
13866         
13867         var r = this.store.getAt(index);
13868         
13869         if(this.tickable){
13870             
13871             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13872                 return;
13873             }
13874             
13875             var rm = false;
13876             var _this = this;
13877             
13878             Roo.each(this.tickItems, function(v,k){
13879                 
13880                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13881                     Roo.log(v);
13882                     _this.tickItems.splice(k, 1);
13883                     
13884                     if(typeof(e) == 'undefined' && view == false){
13885                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13886                     }
13887                     
13888                     rm = true;
13889                     return;
13890                 }
13891             });
13892             
13893             if(rm){
13894                 return;
13895             }
13896             
13897             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13898                 this.tickItems.push(r.data);
13899             }
13900             
13901             if(typeof(e) == 'undefined' && view == false){
13902                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13903             }
13904                     
13905             return;
13906         }
13907         
13908         if(r){
13909             this.onSelect(r, index);
13910         }
13911         if(doFocus !== false && !this.blockFocus){
13912             this.inputEl().focus();
13913         }
13914     },
13915
13916     // private
13917     restrictHeight : function(){
13918         //this.innerList.dom.style.height = '';
13919         //var inner = this.innerList.dom;
13920         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13921         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13922         //this.list.beginUpdate();
13923         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13924         this.list.alignTo(this.inputEl(), this.listAlign);
13925         this.list.alignTo(this.inputEl(), this.listAlign);
13926         //this.list.endUpdate();
13927     },
13928
13929     // private
13930     onEmptyResults : function(){
13931         
13932         if(this.tickable && this.editable){
13933             this.restrictHeight();
13934             return;
13935         }
13936         
13937         this.collapse();
13938     },
13939
13940     /**
13941      * Returns true if the dropdown list is expanded, else false.
13942      */
13943     isExpanded : function(){
13944         return this.list.isVisible();
13945     },
13946
13947     /**
13948      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13949      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13950      * @param {String} value The data value of the item to select
13951      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13952      * selected item if it is not currently in view (defaults to true)
13953      * @return {Boolean} True if the value matched an item in the list, else false
13954      */
13955     selectByValue : function(v, scrollIntoView){
13956         if(v !== undefined && v !== null){
13957             var r = this.findRecord(this.valueField || this.displayField, v);
13958             if(r){
13959                 this.select(this.store.indexOf(r), scrollIntoView);
13960                 return true;
13961             }
13962         }
13963         return false;
13964     },
13965
13966     /**
13967      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13968      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13969      * @param {Number} index The zero-based index of the list item to select
13970      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13971      * selected item if it is not currently in view (defaults to true)
13972      */
13973     select : function(index, scrollIntoView){
13974         this.selectedIndex = index;
13975         this.view.select(index);
13976         if(scrollIntoView !== false){
13977             var el = this.view.getNode(index);
13978             /*
13979              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13980              */
13981             if(el){
13982                 this.list.scrollChildIntoView(el, false);
13983             }
13984         }
13985     },
13986
13987     // private
13988     selectNext : function(){
13989         var ct = this.store.getCount();
13990         if(ct > 0){
13991             if(this.selectedIndex == -1){
13992                 this.select(0);
13993             }else if(this.selectedIndex < ct-1){
13994                 this.select(this.selectedIndex+1);
13995             }
13996         }
13997     },
13998
13999     // private
14000     selectPrev : function(){
14001         var ct = this.store.getCount();
14002         if(ct > 0){
14003             if(this.selectedIndex == -1){
14004                 this.select(0);
14005             }else if(this.selectedIndex != 0){
14006                 this.select(this.selectedIndex-1);
14007             }
14008         }
14009     },
14010
14011     // private
14012     onKeyUp : function(e){
14013         if(this.editable !== false && !e.isSpecialKey()){
14014             this.lastKey = e.getKey();
14015             this.dqTask.delay(this.queryDelay);
14016         }
14017     },
14018
14019     // private
14020     validateBlur : function(){
14021         return !this.list || !this.list.isVisible();   
14022     },
14023
14024     // private
14025     initQuery : function(){
14026         
14027         var v = this.getRawValue();
14028         
14029         if(this.tickable && this.editable){
14030             v = this.tickableInputEl().getValue();
14031         }
14032         
14033         this.doQuery(v);
14034     },
14035
14036     // private
14037     doForce : function(){
14038         if(this.inputEl().dom.value.length > 0){
14039             this.inputEl().dom.value =
14040                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14041              
14042         }
14043     },
14044
14045     /**
14046      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14047      * query allowing the query action to be canceled if needed.
14048      * @param {String} query The SQL query to execute
14049      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14050      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14051      * saved in the current store (defaults to false)
14052      */
14053     doQuery : function(q, forceAll){
14054         
14055         if(q === undefined || q === null){
14056             q = '';
14057         }
14058         var qe = {
14059             query: q,
14060             forceAll: forceAll,
14061             combo: this,
14062             cancel:false
14063         };
14064         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14065             return false;
14066         }
14067         q = qe.query;
14068         
14069         forceAll = qe.forceAll;
14070         if(forceAll === true || (q.length >= this.minChars)){
14071             
14072             this.hasQuery = true;
14073             
14074             if(this.lastQuery != q || this.alwaysQuery){
14075                 this.lastQuery = q;
14076                 if(this.mode == 'local'){
14077                     this.selectedIndex = -1;
14078                     if(forceAll){
14079                         this.store.clearFilter();
14080                     }else{
14081                         
14082                         if(this.specialFilter){
14083                             this.fireEvent('specialfilter', this);
14084                             this.onLoad();
14085                             return;
14086                         }
14087                         
14088                         this.store.filter(this.displayField, q);
14089                     }
14090                     
14091                     this.store.fireEvent("datachanged", this.store);
14092                     
14093                     this.onLoad();
14094                     
14095                     
14096                 }else{
14097                     
14098                     this.store.baseParams[this.queryParam] = q;
14099                     
14100                     var options = {params : this.getParams(q)};
14101                     
14102                     if(this.loadNext){
14103                         options.add = true;
14104                         options.params.start = this.page * this.pageSize;
14105                     }
14106                     
14107                     this.store.load(options);
14108                     
14109                     /*
14110                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14111                      *  we should expand the list on onLoad
14112                      *  so command out it
14113                      */
14114 //                    this.expand();
14115                 }
14116             }else{
14117                 this.selectedIndex = -1;
14118                 this.onLoad();   
14119             }
14120         }
14121         
14122         this.loadNext = false;
14123     },
14124     
14125     // private
14126     getParams : function(q){
14127         var p = {};
14128         //p[this.queryParam] = q;
14129         
14130         if(this.pageSize){
14131             p.start = 0;
14132             p.limit = this.pageSize;
14133         }
14134         return p;
14135     },
14136
14137     /**
14138      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14139      */
14140     collapse : function(){
14141         if(!this.isExpanded()){
14142             return;
14143         }
14144         
14145         this.list.hide();
14146         
14147         this.hasFocus = false;
14148         
14149         if(this.tickable){
14150             this.okBtn.hide();
14151             this.cancelBtn.hide();
14152             this.trigger.show();
14153             
14154             if(this.editable){
14155                 this.tickableInputEl().dom.value = '';
14156                 this.tickableInputEl().blur();
14157             }
14158             
14159         }
14160         
14161         Roo.get(document).un('mousedown', this.collapseIf, this);
14162         Roo.get(document).un('mousewheel', this.collapseIf, this);
14163         if (!this.editable) {
14164             Roo.get(document).un('keydown', this.listKeyPress, this);
14165         }
14166         this.fireEvent('collapse', this);
14167         
14168         this.validate();
14169     },
14170
14171     // private
14172     collapseIf : function(e){
14173         var in_combo  = e.within(this.el);
14174         var in_list =  e.within(this.list);
14175         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14176         
14177         if (in_combo || in_list || is_list) {
14178             //e.stopPropagation();
14179             return;
14180         }
14181         
14182         if(this.tickable){
14183             this.onTickableFooterButtonClick(e, false, false);
14184         }
14185
14186         this.collapse();
14187         
14188     },
14189
14190     /**
14191      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14192      */
14193     expand : function(){
14194        
14195         if(this.isExpanded() || !this.hasFocus){
14196             return;
14197         }
14198         
14199         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14200         this.list.setWidth(lw);
14201         
14202         Roo.log('expand');
14203         
14204         this.list.show();
14205         
14206         this.restrictHeight();
14207         
14208         if(this.tickable){
14209             
14210             this.tickItems = Roo.apply([], this.item);
14211             
14212             this.okBtn.show();
14213             this.cancelBtn.show();
14214             this.trigger.hide();
14215             
14216             if(this.editable){
14217                 this.tickableInputEl().focus();
14218             }
14219             
14220         }
14221         
14222         Roo.get(document).on('mousedown', this.collapseIf, this);
14223         Roo.get(document).on('mousewheel', this.collapseIf, this);
14224         if (!this.editable) {
14225             Roo.get(document).on('keydown', this.listKeyPress, this);
14226         }
14227         
14228         this.fireEvent('expand', this);
14229     },
14230
14231     // private
14232     // Implements the default empty TriggerField.onTriggerClick function
14233     onTriggerClick : function(e)
14234     {
14235         Roo.log('trigger click');
14236         
14237         if(this.disabled || !this.triggerList){
14238             return;
14239         }
14240         
14241         this.page = 0;
14242         this.loadNext = false;
14243         
14244         if(this.isExpanded()){
14245             this.collapse();
14246             if (!this.blockFocus) {
14247                 this.inputEl().focus();
14248             }
14249             
14250         }else {
14251             this.hasFocus = true;
14252             if(this.triggerAction == 'all') {
14253                 this.doQuery(this.allQuery, true);
14254             } else {
14255                 this.doQuery(this.getRawValue());
14256             }
14257             if (!this.blockFocus) {
14258                 this.inputEl().focus();
14259             }
14260         }
14261     },
14262     
14263     onTickableTriggerClick : function(e)
14264     {
14265         if(this.disabled){
14266             return;
14267         }
14268         
14269         this.page = 0;
14270         this.loadNext = false;
14271         this.hasFocus = true;
14272         
14273         if(this.triggerAction == 'all') {
14274             this.doQuery(this.allQuery, true);
14275         } else {
14276             this.doQuery(this.getRawValue());
14277         }
14278     },
14279     
14280     onSearchFieldClick : function(e)
14281     {
14282         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14283             this.onTickableFooterButtonClick(e, false, false);
14284             return;
14285         }
14286         
14287         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14288             return;
14289         }
14290         
14291         this.page = 0;
14292         this.loadNext = false;
14293         this.hasFocus = true;
14294         
14295         if(this.triggerAction == 'all') {
14296             this.doQuery(this.allQuery, true);
14297         } else {
14298             this.doQuery(this.getRawValue());
14299         }
14300     },
14301     
14302     listKeyPress : function(e)
14303     {
14304         //Roo.log('listkeypress');
14305         // scroll to first matching element based on key pres..
14306         if (e.isSpecialKey()) {
14307             return false;
14308         }
14309         var k = String.fromCharCode(e.getKey()).toUpperCase();
14310         //Roo.log(k);
14311         var match  = false;
14312         var csel = this.view.getSelectedNodes();
14313         var cselitem = false;
14314         if (csel.length) {
14315             var ix = this.view.indexOf(csel[0]);
14316             cselitem  = this.store.getAt(ix);
14317             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14318                 cselitem = false;
14319             }
14320             
14321         }
14322         
14323         this.store.each(function(v) { 
14324             if (cselitem) {
14325                 // start at existing selection.
14326                 if (cselitem.id == v.id) {
14327                     cselitem = false;
14328                 }
14329                 return true;
14330             }
14331                 
14332             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14333                 match = this.store.indexOf(v);
14334                 return false;
14335             }
14336             return true;
14337         }, this);
14338         
14339         if (match === false) {
14340             return true; // no more action?
14341         }
14342         // scroll to?
14343         this.view.select(match);
14344         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14345         sn.scrollIntoView(sn.dom.parentNode, false);
14346     },
14347     
14348     onViewScroll : function(e, t){
14349         
14350         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){
14351             return;
14352         }
14353         
14354         this.hasQuery = true;
14355         
14356         this.loading = this.list.select('.loading', true).first();
14357         
14358         if(this.loading === null){
14359             this.list.createChild({
14360                 tag: 'div',
14361                 cls: 'loading roo-select2-more-results roo-select2-active',
14362                 html: 'Loading more results...'
14363             });
14364             
14365             this.loading = this.list.select('.loading', true).first();
14366             
14367             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14368             
14369             this.loading.hide();
14370         }
14371         
14372         this.loading.show();
14373         
14374         var _combo = this;
14375         
14376         this.page++;
14377         this.loadNext = true;
14378         
14379         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14380         
14381         return;
14382     },
14383     
14384     addItem : function(o)
14385     {   
14386         var dv = ''; // display value
14387         
14388         if (this.displayField) {
14389             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14390         } else {
14391             // this is an error condition!!!
14392             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14393         }
14394         
14395         if(!dv.length){
14396             return;
14397         }
14398         
14399         var choice = this.choices.createChild({
14400             tag: 'li',
14401             cls: 'roo-select2-search-choice',
14402             cn: [
14403                 {
14404                     tag: 'div',
14405                     html: dv
14406                 },
14407                 {
14408                     tag: 'a',
14409                     href: '#',
14410                     cls: 'roo-select2-search-choice-close fa fa-times',
14411                     tabindex: '-1'
14412                 }
14413             ]
14414             
14415         }, this.searchField);
14416         
14417         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14418         
14419         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14420         
14421         this.item.push(o);
14422         
14423         this.lastData = o;
14424         
14425         this.syncValue();
14426         
14427         this.inputEl().dom.value = '';
14428         
14429         this.validate();
14430     },
14431     
14432     onRemoveItem : function(e, _self, o)
14433     {
14434         e.preventDefault();
14435         
14436         this.lastItem = Roo.apply([], this.item);
14437         
14438         var index = this.item.indexOf(o.data) * 1;
14439         
14440         if( index < 0){
14441             Roo.log('not this item?!');
14442             return;
14443         }
14444         
14445         this.item.splice(index, 1);
14446         o.item.remove();
14447         
14448         this.syncValue();
14449         
14450         this.fireEvent('remove', this, e);
14451         
14452         this.validate();
14453         
14454     },
14455     
14456     syncValue : function()
14457     {
14458         if(!this.item.length){
14459             this.clearValue();
14460             return;
14461         }
14462             
14463         var value = [];
14464         var _this = this;
14465         Roo.each(this.item, function(i){
14466             if(_this.valueField){
14467                 value.push(i[_this.valueField]);
14468                 return;
14469             }
14470
14471             value.push(i);
14472         });
14473
14474         this.value = value.join(',');
14475
14476         if(this.hiddenField){
14477             this.hiddenField.dom.value = this.value;
14478         }
14479         
14480         this.store.fireEvent("datachanged", this.store);
14481         
14482         this.validate();
14483     },
14484     
14485     clearItem : function()
14486     {
14487         if(!this.multiple){
14488             return;
14489         }
14490         
14491         this.item = [];
14492         
14493         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14494            c.remove();
14495         });
14496         
14497         this.syncValue();
14498         
14499         this.validate();
14500         
14501         if(this.tickable && !Roo.isTouch){
14502             this.view.refresh();
14503         }
14504     },
14505     
14506     inputEl: function ()
14507     {
14508         if(Roo.isIOS && this.useNativeIOS){
14509             return this.el.select('select.roo-ios-select', true).first();
14510         }
14511         
14512         if(Roo.isTouch && this.mobileTouchView){
14513             return this.el.select('input.form-control',true).first();
14514         }
14515         
14516         if(this.tickable){
14517             return this.searchField;
14518         }
14519         
14520         return this.el.select('input.form-control',true).first();
14521     },
14522     
14523     onTickableFooterButtonClick : function(e, btn, el)
14524     {
14525         e.preventDefault();
14526         
14527         this.lastItem = Roo.apply([], this.item);
14528         
14529         if(btn && btn.name == 'cancel'){
14530             this.tickItems = Roo.apply([], this.item);
14531             this.collapse();
14532             return;
14533         }
14534         
14535         this.clearItem();
14536         
14537         var _this = this;
14538         
14539         Roo.each(this.tickItems, function(o){
14540             _this.addItem(o);
14541         });
14542         
14543         this.collapse();
14544         
14545     },
14546     
14547     validate : function()
14548     {
14549         var v = this.getRawValue();
14550         
14551         if(this.multiple){
14552             v = this.getValue();
14553         }
14554         
14555         if(this.disabled || this.allowBlank || v.length){
14556             this.markValid();
14557             return true;
14558         }
14559         
14560         this.markInvalid();
14561         return false;
14562     },
14563     
14564     tickableInputEl : function()
14565     {
14566         if(!this.tickable || !this.editable){
14567             return this.inputEl();
14568         }
14569         
14570         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14571     },
14572     
14573     
14574     getAutoCreateTouchView : function()
14575     {
14576         var id = Roo.id();
14577         
14578         var cfg = {
14579             cls: 'form-group' //input-group
14580         };
14581         
14582         var input =  {
14583             tag: 'input',
14584             id : id,
14585             type : this.inputType,
14586             cls : 'form-control x-combo-noedit',
14587             autocomplete: 'new-password',
14588             placeholder : this.placeholder || '',
14589             readonly : true
14590         };
14591         
14592         if (this.name) {
14593             input.name = this.name;
14594         }
14595         
14596         if (this.size) {
14597             input.cls += ' input-' + this.size;
14598         }
14599         
14600         if (this.disabled) {
14601             input.disabled = true;
14602         }
14603         
14604         var inputblock = {
14605             cls : '',
14606             cn : [
14607                 input
14608             ]
14609         };
14610         
14611         if(this.before){
14612             inputblock.cls += ' input-group';
14613             
14614             inputblock.cn.unshift({
14615                 tag :'span',
14616                 cls : 'input-group-addon',
14617                 html : this.before
14618             });
14619         }
14620         
14621         if(this.removable && !this.multiple){
14622             inputblock.cls += ' roo-removable';
14623             
14624             inputblock.cn.push({
14625                 tag: 'button',
14626                 html : 'x',
14627                 cls : 'roo-combo-removable-btn close'
14628             });
14629         }
14630
14631         if(this.hasFeedback && !this.allowBlank){
14632             
14633             inputblock.cls += ' has-feedback';
14634             
14635             inputblock.cn.push({
14636                 tag: 'span',
14637                 cls: 'glyphicon form-control-feedback'
14638             });
14639             
14640         }
14641         
14642         if (this.after) {
14643             
14644             inputblock.cls += (this.before) ? '' : ' input-group';
14645             
14646             inputblock.cn.push({
14647                 tag :'span',
14648                 cls : 'input-group-addon',
14649                 html : this.after
14650             });
14651         }
14652
14653         var box = {
14654             tag: 'div',
14655             cn: [
14656                 {
14657                     tag: 'input',
14658                     type : 'hidden',
14659                     cls: 'form-hidden-field'
14660                 },
14661                 inputblock
14662             ]
14663             
14664         };
14665         
14666         if(this.multiple){
14667             box = {
14668                 tag: 'div',
14669                 cn: [
14670                     {
14671                         tag: 'input',
14672                         type : 'hidden',
14673                         cls: 'form-hidden-field'
14674                     },
14675                     {
14676                         tag: 'ul',
14677                         cls: 'roo-select2-choices',
14678                         cn:[
14679                             {
14680                                 tag: 'li',
14681                                 cls: 'roo-select2-search-field',
14682                                 cn: [
14683
14684                                     inputblock
14685                                 ]
14686                             }
14687                         ]
14688                     }
14689                 ]
14690             }
14691         };
14692         
14693         var combobox = {
14694             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14695             cn: [
14696                 box
14697             ]
14698         };
14699         
14700         if(!this.multiple && this.showToggleBtn){
14701             
14702             var caret = {
14703                         tag: 'span',
14704                         cls: 'caret'
14705             };
14706             
14707             if (this.caret != false) {
14708                 caret = {
14709                      tag: 'i',
14710                      cls: 'fa fa-' + this.caret
14711                 };
14712                 
14713             }
14714             
14715             combobox.cn.push({
14716                 tag :'span',
14717                 cls : 'input-group-addon btn dropdown-toggle',
14718                 cn : [
14719                     caret,
14720                     {
14721                         tag: 'span',
14722                         cls: 'combobox-clear',
14723                         cn  : [
14724                             {
14725                                 tag : 'i',
14726                                 cls: 'icon-remove'
14727                             }
14728                         ]
14729                     }
14730                 ]
14731
14732             })
14733         }
14734         
14735         if(this.multiple){
14736             combobox.cls += ' roo-select2-container-multi';
14737         }
14738         
14739         var align = this.labelAlign || this.parentLabelAlign();
14740         
14741         if (align ==='left' && this.fieldLabel.length) {
14742
14743             cfg.cn = [
14744                 {
14745                    tag : 'i',
14746                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14747                    tooltip : 'This field is required'
14748                 },
14749                 {
14750                     tag: 'label',
14751                     cls : 'control-label',
14752                     html : this.fieldLabel
14753
14754                 },
14755                 {
14756                     cls : '', 
14757                     cn: [
14758                         combobox
14759                     ]
14760                 }
14761             ];
14762             
14763             var labelCfg = cfg.cn[1];
14764             var contentCfg = cfg.cn[2];
14765             
14766
14767             if(this.indicatorpos == 'right'){
14768                 cfg.cn = [
14769                     {
14770                         tag: 'label',
14771                         'for' :  id,
14772                         cls : 'control-label',
14773                         cn : [
14774                             {
14775                                 tag : 'span',
14776                                 html : this.fieldLabel
14777                             },
14778                             {
14779                                 tag : 'i',
14780                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14781                                 tooltip : 'This field is required'
14782                             }
14783                         ]
14784                     },
14785                     {
14786                         cls : "",
14787                         cn: [
14788                             combobox
14789                         ]
14790                     }
14791
14792                 ];
14793                 
14794                 labelCfg = cfg.cn[0];
14795                 contentCfg = cfg.cn[1];
14796             }
14797             
14798            
14799             
14800             if(this.labelWidth > 12){
14801                 labelCfg.style = "width: " + this.labelWidth + 'px';
14802             }
14803             
14804             if(this.labelWidth < 13 && this.labelmd == 0){
14805                 this.labelmd = this.labelWidth;
14806             }
14807             
14808             if(this.labellg > 0){
14809                 labelCfg.cls += ' col-lg-' + this.labellg;
14810                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14811             }
14812             
14813             if(this.labelmd > 0){
14814                 labelCfg.cls += ' col-md-' + this.labelmd;
14815                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14816             }
14817             
14818             if(this.labelsm > 0){
14819                 labelCfg.cls += ' col-sm-' + this.labelsm;
14820                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14821             }
14822             
14823             if(this.labelxs > 0){
14824                 labelCfg.cls += ' col-xs-' + this.labelxs;
14825                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14826             }
14827                 
14828                 
14829         } else if ( this.fieldLabel.length) {
14830             cfg.cn = [
14831                 {
14832                    tag : 'i',
14833                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14834                    tooltip : 'This field is required'
14835                 },
14836                 {
14837                     tag: 'label',
14838                     cls : 'control-label',
14839                     html : this.fieldLabel
14840
14841                 },
14842                 {
14843                     cls : '', 
14844                     cn: [
14845                         combobox
14846                     ]
14847                 }
14848             ];
14849             
14850             if(this.indicatorpos == 'right'){
14851                 cfg.cn = [
14852                     {
14853                         tag: 'label',
14854                         cls : 'control-label',
14855                         html : this.fieldLabel,
14856                         cn : [
14857                             {
14858                                tag : 'i',
14859                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14860                                tooltip : 'This field is required'
14861                             }
14862                         ]
14863                     },
14864                     {
14865                         cls : '', 
14866                         cn: [
14867                             combobox
14868                         ]
14869                     }
14870                 ];
14871             }
14872         } else {
14873             cfg.cn = combobox;    
14874         }
14875         
14876         
14877         var settings = this;
14878         
14879         ['xs','sm','md','lg'].map(function(size){
14880             if (settings[size]) {
14881                 cfg.cls += ' col-' + size + '-' + settings[size];
14882             }
14883         });
14884         
14885         return cfg;
14886     },
14887     
14888     initTouchView : function()
14889     {
14890         this.renderTouchView();
14891         
14892         this.touchViewEl.on('scroll', function(){
14893             this.el.dom.scrollTop = 0;
14894         }, this);
14895         
14896         this.originalValue = this.getValue();
14897         
14898         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14899         
14900         this.inputEl().on("click", this.showTouchView, this);
14901         if (this.triggerEl) {
14902             this.triggerEl.on("click", this.showTouchView, this);
14903         }
14904         
14905         
14906         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14907         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14908         
14909         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14910         
14911         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14912         this.store.on('load', this.onTouchViewLoad, this);
14913         this.store.on('loadexception', this.onTouchViewLoadException, this);
14914         
14915         if(this.hiddenName){
14916             
14917             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14918             
14919             this.hiddenField.dom.value =
14920                 this.hiddenValue !== undefined ? this.hiddenValue :
14921                 this.value !== undefined ? this.value : '';
14922         
14923             this.el.dom.removeAttribute('name');
14924             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14925         }
14926         
14927         if(this.multiple){
14928             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14929             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14930         }
14931         
14932         if(this.removable && !this.multiple){
14933             var close = this.closeTriggerEl();
14934             if(close){
14935                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14936                 close.on('click', this.removeBtnClick, this, close);
14937             }
14938         }
14939         /*
14940          * fix the bug in Safari iOS8
14941          */
14942         this.inputEl().on("focus", function(e){
14943             document.activeElement.blur();
14944         }, this);
14945         
14946         return;
14947         
14948         
14949     },
14950     
14951     renderTouchView : function()
14952     {
14953         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14954         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14955         
14956         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14957         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14958         
14959         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14960         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14961         this.touchViewBodyEl.setStyle('overflow', 'auto');
14962         
14963         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14964         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14965         
14966         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14967         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14968         
14969     },
14970     
14971     showTouchView : function()
14972     {
14973         if(this.disabled){
14974             return;
14975         }
14976         
14977         this.touchViewHeaderEl.hide();
14978
14979         if(this.modalTitle.length){
14980             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14981             this.touchViewHeaderEl.show();
14982         }
14983
14984         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14985         this.touchViewEl.show();
14986
14987         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14988         
14989         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14990         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14991
14992         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14993
14994         if(this.modalTitle.length){
14995             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14996         }
14997         
14998         this.touchViewBodyEl.setHeight(bodyHeight);
14999
15000         if(this.animate){
15001             var _this = this;
15002             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15003         }else{
15004             this.touchViewEl.addClass('in');
15005         }
15006
15007         this.doTouchViewQuery();
15008         
15009     },
15010     
15011     hideTouchView : function()
15012     {
15013         this.touchViewEl.removeClass('in');
15014
15015         if(this.animate){
15016             var _this = this;
15017             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15018         }else{
15019             this.touchViewEl.setStyle('display', 'none');
15020         }
15021         
15022     },
15023     
15024     setTouchViewValue : function()
15025     {
15026         if(this.multiple){
15027             this.clearItem();
15028         
15029             var _this = this;
15030
15031             Roo.each(this.tickItems, function(o){
15032                 this.addItem(o);
15033             }, this);
15034         }
15035         
15036         this.hideTouchView();
15037     },
15038     
15039     doTouchViewQuery : function()
15040     {
15041         var qe = {
15042             query: '',
15043             forceAll: true,
15044             combo: this,
15045             cancel:false
15046         };
15047         
15048         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15049             return false;
15050         }
15051         
15052         if(!this.alwaysQuery || this.mode == 'local'){
15053             this.onTouchViewLoad();
15054             return;
15055         }
15056         
15057         this.store.load();
15058     },
15059     
15060     onTouchViewBeforeLoad : function(combo,opts)
15061     {
15062         return;
15063     },
15064
15065     // private
15066     onTouchViewLoad : function()
15067     {
15068         if(this.store.getCount() < 1){
15069             this.onTouchViewEmptyResults();
15070             return;
15071         }
15072         
15073         this.clearTouchView();
15074         
15075         var rawValue = this.getRawValue();
15076         
15077         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15078         
15079         this.tickItems = [];
15080         
15081         this.store.data.each(function(d, rowIndex){
15082             var row = this.touchViewListGroup.createChild(template);
15083             
15084             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15085                 row.addClass(d.data.cls);
15086             }
15087             
15088             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15089                 var cfg = {
15090                     data : d.data,
15091                     html : d.data[this.displayField]
15092                 };
15093                 
15094                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15095                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15096                 }
15097             }
15098             row.removeClass('selected');
15099             if(!this.multiple && this.valueField &&
15100                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15101             {
15102                 // radio buttons..
15103                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15104                 row.addClass('selected');
15105             }
15106             
15107             if(this.multiple && this.valueField &&
15108                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15109             {
15110                 
15111                 // checkboxes...
15112                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15113                 this.tickItems.push(d.data);
15114             }
15115             
15116             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15117             
15118         }, this);
15119         
15120         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15121         
15122         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15123
15124         if(this.modalTitle.length){
15125             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15126         }
15127
15128         var listHeight = this.touchViewListGroup.getHeight();
15129         
15130         var _this = this;
15131         
15132         if(firstChecked && listHeight > bodyHeight){
15133             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15134         }
15135         
15136     },
15137     
15138     onTouchViewLoadException : function()
15139     {
15140         this.hideTouchView();
15141     },
15142     
15143     onTouchViewEmptyResults : function()
15144     {
15145         this.clearTouchView();
15146         
15147         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15148         
15149         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15150         
15151     },
15152     
15153     clearTouchView : function()
15154     {
15155         this.touchViewListGroup.dom.innerHTML = '';
15156     },
15157     
15158     onTouchViewClick : function(e, el, o)
15159     {
15160         e.preventDefault();
15161         
15162         var row = o.row;
15163         var rowIndex = o.rowIndex;
15164         
15165         var r = this.store.getAt(rowIndex);
15166         
15167         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15168             
15169             if(!this.multiple){
15170                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15171                     c.dom.removeAttribute('checked');
15172                 }, this);
15173
15174                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15175
15176                 this.setFromData(r.data);
15177
15178                 var close = this.closeTriggerEl();
15179
15180                 if(close){
15181                     close.show();
15182                 }
15183
15184                 this.hideTouchView();
15185
15186                 this.fireEvent('select', this, r, rowIndex);
15187
15188                 return;
15189             }
15190
15191             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15192                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15193                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15194                 return;
15195             }
15196
15197             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15198             this.addItem(r.data);
15199             this.tickItems.push(r.data);
15200         }
15201     },
15202     
15203     getAutoCreateNativeIOS : function()
15204     {
15205         var cfg = {
15206             cls: 'form-group' //input-group,
15207         };
15208         
15209         var combobox =  {
15210             tag: 'select',
15211             cls : 'roo-ios-select'
15212         };
15213         
15214         if (this.name) {
15215             combobox.name = this.name;
15216         }
15217         
15218         if (this.disabled) {
15219             combobox.disabled = true;
15220         }
15221         
15222         var settings = this;
15223         
15224         ['xs','sm','md','lg'].map(function(size){
15225             if (settings[size]) {
15226                 cfg.cls += ' col-' + size + '-' + settings[size];
15227             }
15228         });
15229         
15230         cfg.cn = combobox;
15231         
15232         return cfg;
15233         
15234     },
15235     
15236     initIOSView : function()
15237     {
15238         this.store.on('load', this.onIOSViewLoad, this);
15239         
15240         return;
15241     },
15242     
15243     onIOSViewLoad : function()
15244     {
15245         if(this.store.getCount() < 1){
15246             return;
15247         }
15248         
15249         this.clearIOSView();
15250         
15251         if(this.allowBlank) {
15252             
15253             var default_text = '-- SELECT --';
15254             
15255             if(this.placeholder.length){
15256                 default_text = this.placeholder;
15257             }
15258             
15259             if(this.emptyTitle.length){
15260                 default_text += ' - ' + this.emptyTitle + ' -';
15261             }
15262             
15263             var opt = this.inputEl().createChild({
15264                 tag: 'option',
15265                 value : 0,
15266                 html : default_text
15267             });
15268             
15269             var o = {};
15270             o[this.valueField] = 0;
15271             o[this.displayField] = default_text;
15272             
15273             this.ios_options.push({
15274                 data : o,
15275                 el : opt
15276             });
15277             
15278         }
15279         
15280         this.store.data.each(function(d, rowIndex){
15281             
15282             var html = '';
15283             
15284             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15285                 html = d.data[this.displayField];
15286             }
15287             
15288             var value = '';
15289             
15290             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15291                 value = d.data[this.valueField];
15292             }
15293             
15294             var option = {
15295                 tag: 'option',
15296                 value : value,
15297                 html : html
15298             };
15299             
15300             if(this.value == d.data[this.valueField]){
15301                 option['selected'] = true;
15302             }
15303             
15304             var opt = this.inputEl().createChild(option);
15305             
15306             this.ios_options.push({
15307                 data : d.data,
15308                 el : opt
15309             });
15310             
15311         }, this);
15312         
15313         this.inputEl().on('change', function(){
15314            this.fireEvent('select', this);
15315         }, this);
15316         
15317     },
15318     
15319     clearIOSView: function()
15320     {
15321         this.inputEl().dom.innerHTML = '';
15322         
15323         this.ios_options = [];
15324     },
15325     
15326     setIOSValue: function(v)
15327     {
15328         this.value = v;
15329         
15330         if(!this.ios_options){
15331             return;
15332         }
15333         
15334         Roo.each(this.ios_options, function(opts){
15335            
15336            opts.el.dom.removeAttribute('selected');
15337            
15338            if(opts.data[this.valueField] != v){
15339                return;
15340            }
15341            
15342            opts.el.dom.setAttribute('selected', true);
15343            
15344         }, this);
15345     }
15346
15347     /** 
15348     * @cfg {Boolean} grow 
15349     * @hide 
15350     */
15351     /** 
15352     * @cfg {Number} growMin 
15353     * @hide 
15354     */
15355     /** 
15356     * @cfg {Number} growMax 
15357     * @hide 
15358     */
15359     /**
15360      * @hide
15361      * @method autoSize
15362      */
15363 });
15364
15365 Roo.apply(Roo.bootstrap.ComboBox,  {
15366     
15367     header : {
15368         tag: 'div',
15369         cls: 'modal-header',
15370         cn: [
15371             {
15372                 tag: 'h4',
15373                 cls: 'modal-title'
15374             }
15375         ]
15376     },
15377     
15378     body : {
15379         tag: 'div',
15380         cls: 'modal-body',
15381         cn: [
15382             {
15383                 tag: 'ul',
15384                 cls: 'list-group'
15385             }
15386         ]
15387     },
15388     
15389     listItemRadio : {
15390         tag: 'li',
15391         cls: 'list-group-item',
15392         cn: [
15393             {
15394                 tag: 'span',
15395                 cls: 'roo-combobox-list-group-item-value'
15396             },
15397             {
15398                 tag: 'div',
15399                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15400                 cn: [
15401                     {
15402                         tag: 'input',
15403                         type: 'radio'
15404                     },
15405                     {
15406                         tag: 'label'
15407                     }
15408                 ]
15409             }
15410         ]
15411     },
15412     
15413     listItemCheckbox : {
15414         tag: 'li',
15415         cls: 'list-group-item',
15416         cn: [
15417             {
15418                 tag: 'span',
15419                 cls: 'roo-combobox-list-group-item-value'
15420             },
15421             {
15422                 tag: 'div',
15423                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15424                 cn: [
15425                     {
15426                         tag: 'input',
15427                         type: 'checkbox'
15428                     },
15429                     {
15430                         tag: 'label'
15431                     }
15432                 ]
15433             }
15434         ]
15435     },
15436     
15437     emptyResult : {
15438         tag: 'div',
15439         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15440     },
15441     
15442     footer : {
15443         tag: 'div',
15444         cls: 'modal-footer',
15445         cn: [
15446             {
15447                 tag: 'div',
15448                 cls: 'row',
15449                 cn: [
15450                     {
15451                         tag: 'div',
15452                         cls: 'col-xs-6 text-left',
15453                         cn: {
15454                             tag: 'button',
15455                             cls: 'btn btn-danger roo-touch-view-cancel',
15456                             html: 'Cancel'
15457                         }
15458                     },
15459                     {
15460                         tag: 'div',
15461                         cls: 'col-xs-6 text-right',
15462                         cn: {
15463                             tag: 'button',
15464                             cls: 'btn btn-success roo-touch-view-ok',
15465                             html: 'OK'
15466                         }
15467                     }
15468                 ]
15469             }
15470         ]
15471         
15472     }
15473 });
15474
15475 Roo.apply(Roo.bootstrap.ComboBox,  {
15476     
15477     touchViewTemplate : {
15478         tag: 'div',
15479         cls: 'modal fade roo-combobox-touch-view',
15480         cn: [
15481             {
15482                 tag: 'div',
15483                 cls: 'modal-dialog',
15484                 style : 'position:fixed', // we have to fix position....
15485                 cn: [
15486                     {
15487                         tag: 'div',
15488                         cls: 'modal-content',
15489                         cn: [
15490                             Roo.bootstrap.ComboBox.header,
15491                             Roo.bootstrap.ComboBox.body,
15492                             Roo.bootstrap.ComboBox.footer
15493                         ]
15494                     }
15495                 ]
15496             }
15497         ]
15498     }
15499 });/*
15500  * Based on:
15501  * Ext JS Library 1.1.1
15502  * Copyright(c) 2006-2007, Ext JS, LLC.
15503  *
15504  * Originally Released Under LGPL - original licence link has changed is not relivant.
15505  *
15506  * Fork - LGPL
15507  * <script type="text/javascript">
15508  */
15509
15510 /**
15511  * @class Roo.View
15512  * @extends Roo.util.Observable
15513  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15514  * This class also supports single and multi selection modes. <br>
15515  * Create a data model bound view:
15516  <pre><code>
15517  var store = new Roo.data.Store(...);
15518
15519  var view = new Roo.View({
15520     el : "my-element",
15521     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15522  
15523     singleSelect: true,
15524     selectedClass: "ydataview-selected",
15525     store: store
15526  });
15527
15528  // listen for node click?
15529  view.on("click", function(vw, index, node, e){
15530  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15531  });
15532
15533  // load XML data
15534  dataModel.load("foobar.xml");
15535  </code></pre>
15536  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15537  * <br><br>
15538  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15539  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15540  * 
15541  * Note: old style constructor is still suported (container, template, config)
15542  * 
15543  * @constructor
15544  * Create a new View
15545  * @param {Object} config The config object
15546  * 
15547  */
15548 Roo.View = function(config, depreciated_tpl, depreciated_config){
15549     
15550     this.parent = false;
15551     
15552     if (typeof(depreciated_tpl) == 'undefined') {
15553         // new way.. - universal constructor.
15554         Roo.apply(this, config);
15555         this.el  = Roo.get(this.el);
15556     } else {
15557         // old format..
15558         this.el  = Roo.get(config);
15559         this.tpl = depreciated_tpl;
15560         Roo.apply(this, depreciated_config);
15561     }
15562     this.wrapEl  = this.el.wrap().wrap();
15563     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15564     
15565     
15566     if(typeof(this.tpl) == "string"){
15567         this.tpl = new Roo.Template(this.tpl);
15568     } else {
15569         // support xtype ctors..
15570         this.tpl = new Roo.factory(this.tpl, Roo);
15571     }
15572     
15573     
15574     this.tpl.compile();
15575     
15576     /** @private */
15577     this.addEvents({
15578         /**
15579          * @event beforeclick
15580          * Fires before a click is processed. Returns false to cancel the default action.
15581          * @param {Roo.View} this
15582          * @param {Number} index The index of the target node
15583          * @param {HTMLElement} node The target node
15584          * @param {Roo.EventObject} e The raw event object
15585          */
15586             "beforeclick" : true,
15587         /**
15588          * @event click
15589          * Fires when a template node is clicked.
15590          * @param {Roo.View} this
15591          * @param {Number} index The index of the target node
15592          * @param {HTMLElement} node The target node
15593          * @param {Roo.EventObject} e The raw event object
15594          */
15595             "click" : true,
15596         /**
15597          * @event dblclick
15598          * Fires when a template node is double clicked.
15599          * @param {Roo.View} this
15600          * @param {Number} index The index of the target node
15601          * @param {HTMLElement} node The target node
15602          * @param {Roo.EventObject} e The raw event object
15603          */
15604             "dblclick" : true,
15605         /**
15606          * @event contextmenu
15607          * Fires when a template node is right clicked.
15608          * @param {Roo.View} this
15609          * @param {Number} index The index of the target node
15610          * @param {HTMLElement} node The target node
15611          * @param {Roo.EventObject} e The raw event object
15612          */
15613             "contextmenu" : true,
15614         /**
15615          * @event selectionchange
15616          * Fires when the selected nodes change.
15617          * @param {Roo.View} this
15618          * @param {Array} selections Array of the selected nodes
15619          */
15620             "selectionchange" : true,
15621     
15622         /**
15623          * @event beforeselect
15624          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15625          * @param {Roo.View} this
15626          * @param {HTMLElement} node The node to be selected
15627          * @param {Array} selections Array of currently selected nodes
15628          */
15629             "beforeselect" : true,
15630         /**
15631          * @event preparedata
15632          * Fires on every row to render, to allow you to change the data.
15633          * @param {Roo.View} this
15634          * @param {Object} data to be rendered (change this)
15635          */
15636           "preparedata" : true
15637           
15638           
15639         });
15640
15641
15642
15643     this.el.on({
15644         "click": this.onClick,
15645         "dblclick": this.onDblClick,
15646         "contextmenu": this.onContextMenu,
15647         scope:this
15648     });
15649
15650     this.selections = [];
15651     this.nodes = [];
15652     this.cmp = new Roo.CompositeElementLite([]);
15653     if(this.store){
15654         this.store = Roo.factory(this.store, Roo.data);
15655         this.setStore(this.store, true);
15656     }
15657     
15658     if ( this.footer && this.footer.xtype) {
15659            
15660          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15661         
15662         this.footer.dataSource = this.store;
15663         this.footer.container = fctr;
15664         this.footer = Roo.factory(this.footer, Roo);
15665         fctr.insertFirst(this.el);
15666         
15667         // this is a bit insane - as the paging toolbar seems to detach the el..
15668 //        dom.parentNode.parentNode.parentNode
15669          // they get detached?
15670     }
15671     
15672     
15673     Roo.View.superclass.constructor.call(this);
15674     
15675     
15676 };
15677
15678 Roo.extend(Roo.View, Roo.util.Observable, {
15679     
15680      /**
15681      * @cfg {Roo.data.Store} store Data store to load data from.
15682      */
15683     store : false,
15684     
15685     /**
15686      * @cfg {String|Roo.Element} el The container element.
15687      */
15688     el : '',
15689     
15690     /**
15691      * @cfg {String|Roo.Template} tpl The template used by this View 
15692      */
15693     tpl : false,
15694     /**
15695      * @cfg {String} dataName the named area of the template to use as the data area
15696      *                          Works with domtemplates roo-name="name"
15697      */
15698     dataName: false,
15699     /**
15700      * @cfg {String} selectedClass The css class to add to selected nodes
15701      */
15702     selectedClass : "x-view-selected",
15703      /**
15704      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15705      */
15706     emptyText : "",
15707     
15708     /**
15709      * @cfg {String} text to display on mask (default Loading)
15710      */
15711     mask : false,
15712     /**
15713      * @cfg {Boolean} multiSelect Allow multiple selection
15714      */
15715     multiSelect : false,
15716     /**
15717      * @cfg {Boolean} singleSelect Allow single selection
15718      */
15719     singleSelect:  false,
15720     
15721     /**
15722      * @cfg {Boolean} toggleSelect - selecting 
15723      */
15724     toggleSelect : false,
15725     
15726     /**
15727      * @cfg {Boolean} tickable - selecting 
15728      */
15729     tickable : false,
15730     
15731     /**
15732      * Returns the element this view is bound to.
15733      * @return {Roo.Element}
15734      */
15735     getEl : function(){
15736         return this.wrapEl;
15737     },
15738     
15739     
15740
15741     /**
15742      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15743      */
15744     refresh : function(){
15745         //Roo.log('refresh');
15746         var t = this.tpl;
15747         
15748         // if we are using something like 'domtemplate', then
15749         // the what gets used is:
15750         // t.applySubtemplate(NAME, data, wrapping data..)
15751         // the outer template then get' applied with
15752         //     the store 'extra data'
15753         // and the body get's added to the
15754         //      roo-name="data" node?
15755         //      <span class='roo-tpl-{name}'></span> ?????
15756         
15757         
15758         
15759         this.clearSelections();
15760         this.el.update("");
15761         var html = [];
15762         var records = this.store.getRange();
15763         if(records.length < 1) {
15764             
15765             // is this valid??  = should it render a template??
15766             
15767             this.el.update(this.emptyText);
15768             return;
15769         }
15770         var el = this.el;
15771         if (this.dataName) {
15772             this.el.update(t.apply(this.store.meta)); //????
15773             el = this.el.child('.roo-tpl-' + this.dataName);
15774         }
15775         
15776         for(var i = 0, len = records.length; i < len; i++){
15777             var data = this.prepareData(records[i].data, i, records[i]);
15778             this.fireEvent("preparedata", this, data, i, records[i]);
15779             
15780             var d = Roo.apply({}, data);
15781             
15782             if(this.tickable){
15783                 Roo.apply(d, {'roo-id' : Roo.id()});
15784                 
15785                 var _this = this;
15786             
15787                 Roo.each(this.parent.item, function(item){
15788                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15789                         return;
15790                     }
15791                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15792                 });
15793             }
15794             
15795             html[html.length] = Roo.util.Format.trim(
15796                 this.dataName ?
15797                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15798                     t.apply(d)
15799             );
15800         }
15801         
15802         
15803         
15804         el.update(html.join(""));
15805         this.nodes = el.dom.childNodes;
15806         this.updateIndexes(0);
15807     },
15808     
15809
15810     /**
15811      * Function to override to reformat the data that is sent to
15812      * the template for each node.
15813      * DEPRICATED - use the preparedata event handler.
15814      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15815      * a JSON object for an UpdateManager bound view).
15816      */
15817     prepareData : function(data, index, record)
15818     {
15819         this.fireEvent("preparedata", this, data, index, record);
15820         return data;
15821     },
15822
15823     onUpdate : function(ds, record){
15824         // Roo.log('on update');   
15825         this.clearSelections();
15826         var index = this.store.indexOf(record);
15827         var n = this.nodes[index];
15828         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15829         n.parentNode.removeChild(n);
15830         this.updateIndexes(index, index);
15831     },
15832
15833     
15834     
15835 // --------- FIXME     
15836     onAdd : function(ds, records, index)
15837     {
15838         //Roo.log(['on Add', ds, records, index] );        
15839         this.clearSelections();
15840         if(this.nodes.length == 0){
15841             this.refresh();
15842             return;
15843         }
15844         var n = this.nodes[index];
15845         for(var i = 0, len = records.length; i < len; i++){
15846             var d = this.prepareData(records[i].data, i, records[i]);
15847             if(n){
15848                 this.tpl.insertBefore(n, d);
15849             }else{
15850                 
15851                 this.tpl.append(this.el, d);
15852             }
15853         }
15854         this.updateIndexes(index);
15855     },
15856
15857     onRemove : function(ds, record, index){
15858        // Roo.log('onRemove');
15859         this.clearSelections();
15860         var el = this.dataName  ?
15861             this.el.child('.roo-tpl-' + this.dataName) :
15862             this.el; 
15863         
15864         el.dom.removeChild(this.nodes[index]);
15865         this.updateIndexes(index);
15866     },
15867
15868     /**
15869      * Refresh an individual node.
15870      * @param {Number} index
15871      */
15872     refreshNode : function(index){
15873         this.onUpdate(this.store, this.store.getAt(index));
15874     },
15875
15876     updateIndexes : function(startIndex, endIndex){
15877         var ns = this.nodes;
15878         startIndex = startIndex || 0;
15879         endIndex = endIndex || ns.length - 1;
15880         for(var i = startIndex; i <= endIndex; i++){
15881             ns[i].nodeIndex = i;
15882         }
15883     },
15884
15885     /**
15886      * Changes the data store this view uses and refresh the view.
15887      * @param {Store} store
15888      */
15889     setStore : function(store, initial){
15890         if(!initial && this.store){
15891             this.store.un("datachanged", this.refresh);
15892             this.store.un("add", this.onAdd);
15893             this.store.un("remove", this.onRemove);
15894             this.store.un("update", this.onUpdate);
15895             this.store.un("clear", this.refresh);
15896             this.store.un("beforeload", this.onBeforeLoad);
15897             this.store.un("load", this.onLoad);
15898             this.store.un("loadexception", this.onLoad);
15899         }
15900         if(store){
15901           
15902             store.on("datachanged", this.refresh, this);
15903             store.on("add", this.onAdd, this);
15904             store.on("remove", this.onRemove, this);
15905             store.on("update", this.onUpdate, this);
15906             store.on("clear", this.refresh, this);
15907             store.on("beforeload", this.onBeforeLoad, this);
15908             store.on("load", this.onLoad, this);
15909             store.on("loadexception", this.onLoad, this);
15910         }
15911         
15912         if(store){
15913             this.refresh();
15914         }
15915     },
15916     /**
15917      * onbeforeLoad - masks the loading area.
15918      *
15919      */
15920     onBeforeLoad : function(store,opts)
15921     {
15922          //Roo.log('onBeforeLoad');   
15923         if (!opts.add) {
15924             this.el.update("");
15925         }
15926         this.el.mask(this.mask ? this.mask : "Loading" ); 
15927     },
15928     onLoad : function ()
15929     {
15930         this.el.unmask();
15931     },
15932     
15933
15934     /**
15935      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15936      * @param {HTMLElement} node
15937      * @return {HTMLElement} The template node
15938      */
15939     findItemFromChild : function(node){
15940         var el = this.dataName  ?
15941             this.el.child('.roo-tpl-' + this.dataName,true) :
15942             this.el.dom; 
15943         
15944         if(!node || node.parentNode == el){
15945                     return node;
15946             }
15947             var p = node.parentNode;
15948             while(p && p != el){
15949             if(p.parentNode == el){
15950                 return p;
15951             }
15952             p = p.parentNode;
15953         }
15954             return null;
15955     },
15956
15957     /** @ignore */
15958     onClick : function(e){
15959         var item = this.findItemFromChild(e.getTarget());
15960         if(item){
15961             var index = this.indexOf(item);
15962             if(this.onItemClick(item, index, e) !== false){
15963                 this.fireEvent("click", this, index, item, e);
15964             }
15965         }else{
15966             this.clearSelections();
15967         }
15968     },
15969
15970     /** @ignore */
15971     onContextMenu : function(e){
15972         var item = this.findItemFromChild(e.getTarget());
15973         if(item){
15974             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15975         }
15976     },
15977
15978     /** @ignore */
15979     onDblClick : function(e){
15980         var item = this.findItemFromChild(e.getTarget());
15981         if(item){
15982             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15983         }
15984     },
15985
15986     onItemClick : function(item, index, e)
15987     {
15988         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15989             return false;
15990         }
15991         if (this.toggleSelect) {
15992             var m = this.isSelected(item) ? 'unselect' : 'select';
15993             //Roo.log(m);
15994             var _t = this;
15995             _t[m](item, true, false);
15996             return true;
15997         }
15998         if(this.multiSelect || this.singleSelect){
15999             if(this.multiSelect && e.shiftKey && this.lastSelection){
16000                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16001             }else{
16002                 this.select(item, this.multiSelect && e.ctrlKey);
16003                 this.lastSelection = item;
16004             }
16005             
16006             if(!this.tickable){
16007                 e.preventDefault();
16008             }
16009             
16010         }
16011         return true;
16012     },
16013
16014     /**
16015      * Get the number of selected nodes.
16016      * @return {Number}
16017      */
16018     getSelectionCount : function(){
16019         return this.selections.length;
16020     },
16021
16022     /**
16023      * Get the currently selected nodes.
16024      * @return {Array} An array of HTMLElements
16025      */
16026     getSelectedNodes : function(){
16027         return this.selections;
16028     },
16029
16030     /**
16031      * Get the indexes of the selected nodes.
16032      * @return {Array}
16033      */
16034     getSelectedIndexes : function(){
16035         var indexes = [], s = this.selections;
16036         for(var i = 0, len = s.length; i < len; i++){
16037             indexes.push(s[i].nodeIndex);
16038         }
16039         return indexes;
16040     },
16041
16042     /**
16043      * Clear all selections
16044      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16045      */
16046     clearSelections : function(suppressEvent){
16047         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16048             this.cmp.elements = this.selections;
16049             this.cmp.removeClass(this.selectedClass);
16050             this.selections = [];
16051             if(!suppressEvent){
16052                 this.fireEvent("selectionchange", this, this.selections);
16053             }
16054         }
16055     },
16056
16057     /**
16058      * Returns true if the passed node is selected
16059      * @param {HTMLElement/Number} node The node or node index
16060      * @return {Boolean}
16061      */
16062     isSelected : function(node){
16063         var s = this.selections;
16064         if(s.length < 1){
16065             return false;
16066         }
16067         node = this.getNode(node);
16068         return s.indexOf(node) !== -1;
16069     },
16070
16071     /**
16072      * Selects nodes.
16073      * @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
16074      * @param {Boolean} keepExisting (optional) true to keep existing selections
16075      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16076      */
16077     select : function(nodeInfo, keepExisting, suppressEvent){
16078         if(nodeInfo instanceof Array){
16079             if(!keepExisting){
16080                 this.clearSelections(true);
16081             }
16082             for(var i = 0, len = nodeInfo.length; i < len; i++){
16083                 this.select(nodeInfo[i], true, true);
16084             }
16085             return;
16086         } 
16087         var node = this.getNode(nodeInfo);
16088         if(!node || this.isSelected(node)){
16089             return; // already selected.
16090         }
16091         if(!keepExisting){
16092             this.clearSelections(true);
16093         }
16094         
16095         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16096             Roo.fly(node).addClass(this.selectedClass);
16097             this.selections.push(node);
16098             if(!suppressEvent){
16099                 this.fireEvent("selectionchange", this, this.selections);
16100             }
16101         }
16102         
16103         
16104     },
16105       /**
16106      * Unselects nodes.
16107      * @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
16108      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16109      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16110      */
16111     unselect : function(nodeInfo, keepExisting, suppressEvent)
16112     {
16113         if(nodeInfo instanceof Array){
16114             Roo.each(this.selections, function(s) {
16115                 this.unselect(s, nodeInfo);
16116             }, this);
16117             return;
16118         }
16119         var node = this.getNode(nodeInfo);
16120         if(!node || !this.isSelected(node)){
16121             //Roo.log("not selected");
16122             return; // not selected.
16123         }
16124         // fireevent???
16125         var ns = [];
16126         Roo.each(this.selections, function(s) {
16127             if (s == node ) {
16128                 Roo.fly(node).removeClass(this.selectedClass);
16129
16130                 return;
16131             }
16132             ns.push(s);
16133         },this);
16134         
16135         this.selections= ns;
16136         this.fireEvent("selectionchange", this, this.selections);
16137     },
16138
16139     /**
16140      * Gets a template node.
16141      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16142      * @return {HTMLElement} The node or null if it wasn't found
16143      */
16144     getNode : function(nodeInfo){
16145         if(typeof nodeInfo == "string"){
16146             return document.getElementById(nodeInfo);
16147         }else if(typeof nodeInfo == "number"){
16148             return this.nodes[nodeInfo];
16149         }
16150         return nodeInfo;
16151     },
16152
16153     /**
16154      * Gets a range template nodes.
16155      * @param {Number} startIndex
16156      * @param {Number} endIndex
16157      * @return {Array} An array of nodes
16158      */
16159     getNodes : function(start, end){
16160         var ns = this.nodes;
16161         start = start || 0;
16162         end = typeof end == "undefined" ? ns.length - 1 : end;
16163         var nodes = [];
16164         if(start <= end){
16165             for(var i = start; i <= end; i++){
16166                 nodes.push(ns[i]);
16167             }
16168         } else{
16169             for(var i = start; i >= end; i--){
16170                 nodes.push(ns[i]);
16171             }
16172         }
16173         return nodes;
16174     },
16175
16176     /**
16177      * Finds the index of the passed node
16178      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16179      * @return {Number} The index of the node or -1
16180      */
16181     indexOf : function(node){
16182         node = this.getNode(node);
16183         if(typeof node.nodeIndex == "number"){
16184             return node.nodeIndex;
16185         }
16186         var ns = this.nodes;
16187         for(var i = 0, len = ns.length; i < len; i++){
16188             if(ns[i] == node){
16189                 return i;
16190             }
16191         }
16192         return -1;
16193     }
16194 });
16195 /*
16196  * - LGPL
16197  *
16198  * based on jquery fullcalendar
16199  * 
16200  */
16201
16202 Roo.bootstrap = Roo.bootstrap || {};
16203 /**
16204  * @class Roo.bootstrap.Calendar
16205  * @extends Roo.bootstrap.Component
16206  * Bootstrap Calendar class
16207  * @cfg {Boolean} loadMask (true|false) default false
16208  * @cfg {Object} header generate the user specific header of the calendar, default false
16209
16210  * @constructor
16211  * Create a new Container
16212  * @param {Object} config The config object
16213  */
16214
16215
16216
16217 Roo.bootstrap.Calendar = function(config){
16218     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16219      this.addEvents({
16220         /**
16221              * @event select
16222              * Fires when a date is selected
16223              * @param {DatePicker} this
16224              * @param {Date} date The selected date
16225              */
16226         'select': true,
16227         /**
16228              * @event monthchange
16229              * Fires when the displayed month changes 
16230              * @param {DatePicker} this
16231              * @param {Date} date The selected month
16232              */
16233         'monthchange': true,
16234         /**
16235              * @event evententer
16236              * Fires when mouse over an event
16237              * @param {Calendar} this
16238              * @param {event} Event
16239              */
16240         'evententer': true,
16241         /**
16242              * @event eventleave
16243              * Fires when the mouse leaves an
16244              * @param {Calendar} this
16245              * @param {event}
16246              */
16247         'eventleave': true,
16248         /**
16249              * @event eventclick
16250              * Fires when the mouse click an
16251              * @param {Calendar} this
16252              * @param {event}
16253              */
16254         'eventclick': true
16255         
16256     });
16257
16258 };
16259
16260 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16261     
16262      /**
16263      * @cfg {Number} startDay
16264      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16265      */
16266     startDay : 0,
16267     
16268     loadMask : false,
16269     
16270     header : false,
16271       
16272     getAutoCreate : function(){
16273         
16274         
16275         var fc_button = function(name, corner, style, content ) {
16276             return Roo.apply({},{
16277                 tag : 'span',
16278                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16279                          (corner.length ?
16280                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16281                             ''
16282                         ),
16283                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16284                 unselectable: 'on'
16285             });
16286         };
16287         
16288         var header = {};
16289         
16290         if(!this.header){
16291             header = {
16292                 tag : 'table',
16293                 cls : 'fc-header',
16294                 style : 'width:100%',
16295                 cn : [
16296                     {
16297                         tag: 'tr',
16298                         cn : [
16299                             {
16300                                 tag : 'td',
16301                                 cls : 'fc-header-left',
16302                                 cn : [
16303                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16304                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16305                                     { tag: 'span', cls: 'fc-header-space' },
16306                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16307
16308
16309                                 ]
16310                             },
16311
16312                             {
16313                                 tag : 'td',
16314                                 cls : 'fc-header-center',
16315                                 cn : [
16316                                     {
16317                                         tag: 'span',
16318                                         cls: 'fc-header-title',
16319                                         cn : {
16320                                             tag: 'H2',
16321                                             html : 'month / year'
16322                                         }
16323                                     }
16324
16325                                 ]
16326                             },
16327                             {
16328                                 tag : 'td',
16329                                 cls : 'fc-header-right',
16330                                 cn : [
16331                               /*      fc_button('month', 'left', '', 'month' ),
16332                                     fc_button('week', '', '', 'week' ),
16333                                     fc_button('day', 'right', '', 'day' )
16334                                 */    
16335
16336                                 ]
16337                             }
16338
16339                         ]
16340                     }
16341                 ]
16342             };
16343         }
16344         
16345         header = this.header;
16346         
16347        
16348         var cal_heads = function() {
16349             var ret = [];
16350             // fixme - handle this.
16351             
16352             for (var i =0; i < Date.dayNames.length; i++) {
16353                 var d = Date.dayNames[i];
16354                 ret.push({
16355                     tag: 'th',
16356                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16357                     html : d.substring(0,3)
16358                 });
16359                 
16360             }
16361             ret[0].cls += ' fc-first';
16362             ret[6].cls += ' fc-last';
16363             return ret;
16364         };
16365         var cal_cell = function(n) {
16366             return  {
16367                 tag: 'td',
16368                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16369                 cn : [
16370                     {
16371                         cn : [
16372                             {
16373                                 cls: 'fc-day-number',
16374                                 html: 'D'
16375                             },
16376                             {
16377                                 cls: 'fc-day-content',
16378                              
16379                                 cn : [
16380                                      {
16381                                         style: 'position: relative;' // height: 17px;
16382                                     }
16383                                 ]
16384                             }
16385                             
16386                             
16387                         ]
16388                     }
16389                 ]
16390                 
16391             }
16392         };
16393         var cal_rows = function() {
16394             
16395             var ret = [];
16396             for (var r = 0; r < 6; r++) {
16397                 var row= {
16398                     tag : 'tr',
16399                     cls : 'fc-week',
16400                     cn : []
16401                 };
16402                 
16403                 for (var i =0; i < Date.dayNames.length; i++) {
16404                     var d = Date.dayNames[i];
16405                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16406
16407                 }
16408                 row.cn[0].cls+=' fc-first';
16409                 row.cn[0].cn[0].style = 'min-height:90px';
16410                 row.cn[6].cls+=' fc-last';
16411                 ret.push(row);
16412                 
16413             }
16414             ret[0].cls += ' fc-first';
16415             ret[4].cls += ' fc-prev-last';
16416             ret[5].cls += ' fc-last';
16417             return ret;
16418             
16419         };
16420         
16421         var cal_table = {
16422             tag: 'table',
16423             cls: 'fc-border-separate',
16424             style : 'width:100%',
16425             cellspacing  : 0,
16426             cn : [
16427                 { 
16428                     tag: 'thead',
16429                     cn : [
16430                         { 
16431                             tag: 'tr',
16432                             cls : 'fc-first fc-last',
16433                             cn : cal_heads()
16434                         }
16435                     ]
16436                 },
16437                 { 
16438                     tag: 'tbody',
16439                     cn : cal_rows()
16440                 }
16441                   
16442             ]
16443         };
16444          
16445          var cfg = {
16446             cls : 'fc fc-ltr',
16447             cn : [
16448                 header,
16449                 {
16450                     cls : 'fc-content',
16451                     style : "position: relative;",
16452                     cn : [
16453                         {
16454                             cls : 'fc-view fc-view-month fc-grid',
16455                             style : 'position: relative',
16456                             unselectable : 'on',
16457                             cn : [
16458                                 {
16459                                     cls : 'fc-event-container',
16460                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16461                                 },
16462                                 cal_table
16463                             ]
16464                         }
16465                     ]
16466     
16467                 }
16468            ] 
16469             
16470         };
16471         
16472          
16473         
16474         return cfg;
16475     },
16476     
16477     
16478     initEvents : function()
16479     {
16480         if(!this.store){
16481             throw "can not find store for calendar";
16482         }
16483         
16484         var mark = {
16485             tag: "div",
16486             cls:"x-dlg-mask",
16487             style: "text-align:center",
16488             cn: [
16489                 {
16490                     tag: "div",
16491                     style: "background-color:white;width:50%;margin:250 auto",
16492                     cn: [
16493                         {
16494                             tag: "img",
16495                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16496                         },
16497                         {
16498                             tag: "span",
16499                             html: "Loading"
16500                         }
16501                         
16502                     ]
16503                 }
16504             ]
16505         };
16506         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16507         
16508         var size = this.el.select('.fc-content', true).first().getSize();
16509         this.maskEl.setSize(size.width, size.height);
16510         this.maskEl.enableDisplayMode("block");
16511         if(!this.loadMask){
16512             this.maskEl.hide();
16513         }
16514         
16515         this.store = Roo.factory(this.store, Roo.data);
16516         this.store.on('load', this.onLoad, this);
16517         this.store.on('beforeload', this.onBeforeLoad, this);
16518         
16519         this.resize();
16520         
16521         this.cells = this.el.select('.fc-day',true);
16522         //Roo.log(this.cells);
16523         this.textNodes = this.el.query('.fc-day-number');
16524         this.cells.addClassOnOver('fc-state-hover');
16525         
16526         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16527         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16528         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16529         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16530         
16531         this.on('monthchange', this.onMonthChange, this);
16532         
16533         this.update(new Date().clearTime());
16534     },
16535     
16536     resize : function() {
16537         var sz  = this.el.getSize();
16538         
16539         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16540         this.el.select('.fc-day-content div',true).setHeight(34);
16541     },
16542     
16543     
16544     // private
16545     showPrevMonth : function(e){
16546         this.update(this.activeDate.add("mo", -1));
16547     },
16548     showToday : function(e){
16549         this.update(new Date().clearTime());
16550     },
16551     // private
16552     showNextMonth : function(e){
16553         this.update(this.activeDate.add("mo", 1));
16554     },
16555
16556     // private
16557     showPrevYear : function(){
16558         this.update(this.activeDate.add("y", -1));
16559     },
16560
16561     // private
16562     showNextYear : function(){
16563         this.update(this.activeDate.add("y", 1));
16564     },
16565
16566     
16567    // private
16568     update : function(date)
16569     {
16570         var vd = this.activeDate;
16571         this.activeDate = date;
16572 //        if(vd && this.el){
16573 //            var t = date.getTime();
16574 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16575 //                Roo.log('using add remove');
16576 //                
16577 //                this.fireEvent('monthchange', this, date);
16578 //                
16579 //                this.cells.removeClass("fc-state-highlight");
16580 //                this.cells.each(function(c){
16581 //                   if(c.dateValue == t){
16582 //                       c.addClass("fc-state-highlight");
16583 //                       setTimeout(function(){
16584 //                            try{c.dom.firstChild.focus();}catch(e){}
16585 //                       }, 50);
16586 //                       return false;
16587 //                   }
16588 //                   return true;
16589 //                });
16590 //                return;
16591 //            }
16592 //        }
16593         
16594         var days = date.getDaysInMonth();
16595         
16596         var firstOfMonth = date.getFirstDateOfMonth();
16597         var startingPos = firstOfMonth.getDay()-this.startDay;
16598         
16599         if(startingPos < this.startDay){
16600             startingPos += 7;
16601         }
16602         
16603         var pm = date.add(Date.MONTH, -1);
16604         var prevStart = pm.getDaysInMonth()-startingPos;
16605 //        
16606         this.cells = this.el.select('.fc-day',true);
16607         this.textNodes = this.el.query('.fc-day-number');
16608         this.cells.addClassOnOver('fc-state-hover');
16609         
16610         var cells = this.cells.elements;
16611         var textEls = this.textNodes;
16612         
16613         Roo.each(cells, function(cell){
16614             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16615         });
16616         
16617         days += startingPos;
16618
16619         // convert everything to numbers so it's fast
16620         var day = 86400000;
16621         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16622         //Roo.log(d);
16623         //Roo.log(pm);
16624         //Roo.log(prevStart);
16625         
16626         var today = new Date().clearTime().getTime();
16627         var sel = date.clearTime().getTime();
16628         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16629         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16630         var ddMatch = this.disabledDatesRE;
16631         var ddText = this.disabledDatesText;
16632         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16633         var ddaysText = this.disabledDaysText;
16634         var format = this.format;
16635         
16636         var setCellClass = function(cal, cell){
16637             cell.row = 0;
16638             cell.events = [];
16639             cell.more = [];
16640             //Roo.log('set Cell Class');
16641             cell.title = "";
16642             var t = d.getTime();
16643             
16644             //Roo.log(d);
16645             
16646             cell.dateValue = t;
16647             if(t == today){
16648                 cell.className += " fc-today";
16649                 cell.className += " fc-state-highlight";
16650                 cell.title = cal.todayText;
16651             }
16652             if(t == sel){
16653                 // disable highlight in other month..
16654                 //cell.className += " fc-state-highlight";
16655                 
16656             }
16657             // disabling
16658             if(t < min) {
16659                 cell.className = " fc-state-disabled";
16660                 cell.title = cal.minText;
16661                 return;
16662             }
16663             if(t > max) {
16664                 cell.className = " fc-state-disabled";
16665                 cell.title = cal.maxText;
16666                 return;
16667             }
16668             if(ddays){
16669                 if(ddays.indexOf(d.getDay()) != -1){
16670                     cell.title = ddaysText;
16671                     cell.className = " fc-state-disabled";
16672                 }
16673             }
16674             if(ddMatch && format){
16675                 var fvalue = d.dateFormat(format);
16676                 if(ddMatch.test(fvalue)){
16677                     cell.title = ddText.replace("%0", fvalue);
16678                     cell.className = " fc-state-disabled";
16679                 }
16680             }
16681             
16682             if (!cell.initialClassName) {
16683                 cell.initialClassName = cell.dom.className;
16684             }
16685             
16686             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16687         };
16688
16689         var i = 0;
16690         
16691         for(; i < startingPos; i++) {
16692             textEls[i].innerHTML = (++prevStart);
16693             d.setDate(d.getDate()+1);
16694             
16695             cells[i].className = "fc-past fc-other-month";
16696             setCellClass(this, cells[i]);
16697         }
16698         
16699         var intDay = 0;
16700         
16701         for(; i < days; i++){
16702             intDay = i - startingPos + 1;
16703             textEls[i].innerHTML = (intDay);
16704             d.setDate(d.getDate()+1);
16705             
16706             cells[i].className = ''; // "x-date-active";
16707             setCellClass(this, cells[i]);
16708         }
16709         var extraDays = 0;
16710         
16711         for(; i < 42; i++) {
16712             textEls[i].innerHTML = (++extraDays);
16713             d.setDate(d.getDate()+1);
16714             
16715             cells[i].className = "fc-future fc-other-month";
16716             setCellClass(this, cells[i]);
16717         }
16718         
16719         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16720         
16721         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16722         
16723         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16724         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16725         
16726         if(totalRows != 6){
16727             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16728             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16729         }
16730         
16731         this.fireEvent('monthchange', this, date);
16732         
16733         
16734         /*
16735         if(!this.internalRender){
16736             var main = this.el.dom.firstChild;
16737             var w = main.offsetWidth;
16738             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16739             Roo.fly(main).setWidth(w);
16740             this.internalRender = true;
16741             // opera does not respect the auto grow header center column
16742             // then, after it gets a width opera refuses to recalculate
16743             // without a second pass
16744             if(Roo.isOpera && !this.secondPass){
16745                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16746                 this.secondPass = true;
16747                 this.update.defer(10, this, [date]);
16748             }
16749         }
16750         */
16751         
16752     },
16753     
16754     findCell : function(dt) {
16755         dt = dt.clearTime().getTime();
16756         var ret = false;
16757         this.cells.each(function(c){
16758             //Roo.log("check " +c.dateValue + '?=' + dt);
16759             if(c.dateValue == dt){
16760                 ret = c;
16761                 return false;
16762             }
16763             return true;
16764         });
16765         
16766         return ret;
16767     },
16768     
16769     findCells : function(ev) {
16770         var s = ev.start.clone().clearTime().getTime();
16771        // Roo.log(s);
16772         var e= ev.end.clone().clearTime().getTime();
16773        // Roo.log(e);
16774         var ret = [];
16775         this.cells.each(function(c){
16776              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16777             
16778             if(c.dateValue > e){
16779                 return ;
16780             }
16781             if(c.dateValue < s){
16782                 return ;
16783             }
16784             ret.push(c);
16785         });
16786         
16787         return ret;    
16788     },
16789     
16790 //    findBestRow: function(cells)
16791 //    {
16792 //        var ret = 0;
16793 //        
16794 //        for (var i =0 ; i < cells.length;i++) {
16795 //            ret  = Math.max(cells[i].rows || 0,ret);
16796 //        }
16797 //        return ret;
16798 //        
16799 //    },
16800     
16801     
16802     addItem : function(ev)
16803     {
16804         // look for vertical location slot in
16805         var cells = this.findCells(ev);
16806         
16807 //        ev.row = this.findBestRow(cells);
16808         
16809         // work out the location.
16810         
16811         var crow = false;
16812         var rows = [];
16813         for(var i =0; i < cells.length; i++) {
16814             
16815             cells[i].row = cells[0].row;
16816             
16817             if(i == 0){
16818                 cells[i].row = cells[i].row + 1;
16819             }
16820             
16821             if (!crow) {
16822                 crow = {
16823                     start : cells[i],
16824                     end :  cells[i]
16825                 };
16826                 continue;
16827             }
16828             if (crow.start.getY() == cells[i].getY()) {
16829                 // on same row.
16830                 crow.end = cells[i];
16831                 continue;
16832             }
16833             // different row.
16834             rows.push(crow);
16835             crow = {
16836                 start: cells[i],
16837                 end : cells[i]
16838             };
16839             
16840         }
16841         
16842         rows.push(crow);
16843         ev.els = [];
16844         ev.rows = rows;
16845         ev.cells = cells;
16846         
16847         cells[0].events.push(ev);
16848         
16849         this.calevents.push(ev);
16850     },
16851     
16852     clearEvents: function() {
16853         
16854         if(!this.calevents){
16855             return;
16856         }
16857         
16858         Roo.each(this.cells.elements, function(c){
16859             c.row = 0;
16860             c.events = [];
16861             c.more = [];
16862         });
16863         
16864         Roo.each(this.calevents, function(e) {
16865             Roo.each(e.els, function(el) {
16866                 el.un('mouseenter' ,this.onEventEnter, this);
16867                 el.un('mouseleave' ,this.onEventLeave, this);
16868                 el.remove();
16869             },this);
16870         },this);
16871         
16872         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16873             e.remove();
16874         });
16875         
16876     },
16877     
16878     renderEvents: function()
16879     {   
16880         var _this = this;
16881         
16882         this.cells.each(function(c) {
16883             
16884             if(c.row < 5){
16885                 return;
16886             }
16887             
16888             var ev = c.events;
16889             
16890             var r = 4;
16891             if(c.row != c.events.length){
16892                 r = 4 - (4 - (c.row - c.events.length));
16893             }
16894             
16895             c.events = ev.slice(0, r);
16896             c.more = ev.slice(r);
16897             
16898             if(c.more.length && c.more.length == 1){
16899                 c.events.push(c.more.pop());
16900             }
16901             
16902             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16903             
16904         });
16905             
16906         this.cells.each(function(c) {
16907             
16908             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16909             
16910             
16911             for (var e = 0; e < c.events.length; e++){
16912                 var ev = c.events[e];
16913                 var rows = ev.rows;
16914                 
16915                 for(var i = 0; i < rows.length; i++) {
16916                 
16917                     // how many rows should it span..
16918
16919                     var  cfg = {
16920                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16921                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16922
16923                         unselectable : "on",
16924                         cn : [
16925                             {
16926                                 cls: 'fc-event-inner',
16927                                 cn : [
16928     //                                {
16929     //                                  tag:'span',
16930     //                                  cls: 'fc-event-time',
16931     //                                  html : cells.length > 1 ? '' : ev.time
16932     //                                },
16933                                     {
16934                                       tag:'span',
16935                                       cls: 'fc-event-title',
16936                                       html : String.format('{0}', ev.title)
16937                                     }
16938
16939
16940                                 ]
16941                             },
16942                             {
16943                                 cls: 'ui-resizable-handle ui-resizable-e',
16944                                 html : '&nbsp;&nbsp;&nbsp'
16945                             }
16946
16947                         ]
16948                     };
16949
16950                     if (i == 0) {
16951                         cfg.cls += ' fc-event-start';
16952                     }
16953                     if ((i+1) == rows.length) {
16954                         cfg.cls += ' fc-event-end';
16955                     }
16956
16957                     var ctr = _this.el.select('.fc-event-container',true).first();
16958                     var cg = ctr.createChild(cfg);
16959
16960                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16961                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16962
16963                     var r = (c.more.length) ? 1 : 0;
16964                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16965                     cg.setWidth(ebox.right - sbox.x -2);
16966
16967                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16968                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16969                     cg.on('click', _this.onEventClick, _this, ev);
16970
16971                     ev.els.push(cg);
16972                     
16973                 }
16974                 
16975             }
16976             
16977             
16978             if(c.more.length){
16979                 var  cfg = {
16980                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16981                     style : 'position: absolute',
16982                     unselectable : "on",
16983                     cn : [
16984                         {
16985                             cls: 'fc-event-inner',
16986                             cn : [
16987                                 {
16988                                   tag:'span',
16989                                   cls: 'fc-event-title',
16990                                   html : 'More'
16991                                 }
16992
16993
16994                             ]
16995                         },
16996                         {
16997                             cls: 'ui-resizable-handle ui-resizable-e',
16998                             html : '&nbsp;&nbsp;&nbsp'
16999                         }
17000
17001                     ]
17002                 };
17003
17004                 var ctr = _this.el.select('.fc-event-container',true).first();
17005                 var cg = ctr.createChild(cfg);
17006
17007                 var sbox = c.select('.fc-day-content',true).first().getBox();
17008                 var ebox = c.select('.fc-day-content',true).first().getBox();
17009                 //Roo.log(cg);
17010                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17011                 cg.setWidth(ebox.right - sbox.x -2);
17012
17013                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17014                 
17015             }
17016             
17017         });
17018         
17019         
17020         
17021     },
17022     
17023     onEventEnter: function (e, el,event,d) {
17024         this.fireEvent('evententer', this, el, event);
17025     },
17026     
17027     onEventLeave: function (e, el,event,d) {
17028         this.fireEvent('eventleave', this, el, event);
17029     },
17030     
17031     onEventClick: function (e, el,event,d) {
17032         this.fireEvent('eventclick', this, el, event);
17033     },
17034     
17035     onMonthChange: function () {
17036         this.store.load();
17037     },
17038     
17039     onMoreEventClick: function(e, el, more)
17040     {
17041         var _this = this;
17042         
17043         this.calpopover.placement = 'right';
17044         this.calpopover.setTitle('More');
17045         
17046         this.calpopover.setContent('');
17047         
17048         var ctr = this.calpopover.el.select('.popover-content', true).first();
17049         
17050         Roo.each(more, function(m){
17051             var cfg = {
17052                 cls : 'fc-event-hori fc-event-draggable',
17053                 html : m.title
17054             };
17055             var cg = ctr.createChild(cfg);
17056             
17057             cg.on('click', _this.onEventClick, _this, m);
17058         });
17059         
17060         this.calpopover.show(el);
17061         
17062         
17063     },
17064     
17065     onLoad: function () 
17066     {   
17067         this.calevents = [];
17068         var cal = this;
17069         
17070         if(this.store.getCount() > 0){
17071             this.store.data.each(function(d){
17072                cal.addItem({
17073                     id : d.data.id,
17074                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17075                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17076                     time : d.data.start_time,
17077                     title : d.data.title,
17078                     description : d.data.description,
17079                     venue : d.data.venue
17080                 });
17081             });
17082         }
17083         
17084         this.renderEvents();
17085         
17086         if(this.calevents.length && this.loadMask){
17087             this.maskEl.hide();
17088         }
17089     },
17090     
17091     onBeforeLoad: function()
17092     {
17093         this.clearEvents();
17094         if(this.loadMask){
17095             this.maskEl.show();
17096         }
17097     }
17098 });
17099
17100  
17101  /*
17102  * - LGPL
17103  *
17104  * element
17105  * 
17106  */
17107
17108 /**
17109  * @class Roo.bootstrap.Popover
17110  * @extends Roo.bootstrap.Component
17111  * Bootstrap Popover class
17112  * @cfg {String} html contents of the popover   (or false to use children..)
17113  * @cfg {String} title of popover (or false to hide)
17114  * @cfg {String} placement how it is placed
17115  * @cfg {String} trigger click || hover (or false to trigger manually)
17116  * @cfg {String} over what (parent or false to trigger manually.)
17117  * @cfg {Number} delay - delay before showing
17118  
17119  * @constructor
17120  * Create a new Popover
17121  * @param {Object} config The config object
17122  */
17123
17124 Roo.bootstrap.Popover = function(config){
17125     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17126     
17127     this.addEvents({
17128         // raw events
17129          /**
17130          * @event show
17131          * After the popover show
17132          * 
17133          * @param {Roo.bootstrap.Popover} this
17134          */
17135         "show" : true,
17136         /**
17137          * @event hide
17138          * After the popover hide
17139          * 
17140          * @param {Roo.bootstrap.Popover} this
17141          */
17142         "hide" : true
17143     });
17144 };
17145
17146 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17147     
17148     title: 'Fill in a title',
17149     html: false,
17150     
17151     placement : 'right',
17152     trigger : 'hover', // hover
17153     
17154     delay : 0,
17155     
17156     over: 'parent',
17157     
17158     can_build_overlaid : false,
17159     
17160     getChildContainer : function()
17161     {
17162         return this.el.select('.popover-content',true).first();
17163     },
17164     
17165     getAutoCreate : function(){
17166          
17167         var cfg = {
17168            cls : 'popover roo-dynamic',
17169            style: 'display:block',
17170            cn : [
17171                 {
17172                     cls : 'arrow'
17173                 },
17174                 {
17175                     cls : 'popover-inner',
17176                     cn : [
17177                         {
17178                             tag: 'h3',
17179                             cls: 'popover-title',
17180                             html : this.title
17181                         },
17182                         {
17183                             cls : 'popover-content',
17184                             html : this.html
17185                         }
17186                     ]
17187                     
17188                 }
17189            ]
17190         };
17191         
17192         return cfg;
17193     },
17194     setTitle: function(str)
17195     {
17196         this.title = str;
17197         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17198     },
17199     setContent: function(str)
17200     {
17201         this.html = str;
17202         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17203     },
17204     // as it get's added to the bottom of the page.
17205     onRender : function(ct, position)
17206     {
17207         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17208         if(!this.el){
17209             var cfg = Roo.apply({},  this.getAutoCreate());
17210             cfg.id = Roo.id();
17211             
17212             if (this.cls) {
17213                 cfg.cls += ' ' + this.cls;
17214             }
17215             if (this.style) {
17216                 cfg.style = this.style;
17217             }
17218             //Roo.log("adding to ");
17219             this.el = Roo.get(document.body).createChild(cfg, position);
17220 //            Roo.log(this.el);
17221         }
17222         this.initEvents();
17223     },
17224     
17225     initEvents : function()
17226     {
17227         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17228         this.el.enableDisplayMode('block');
17229         this.el.hide();
17230         if (this.over === false) {
17231             return; 
17232         }
17233         if (this.triggers === false) {
17234             return;
17235         }
17236         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17237         var triggers = this.trigger ? this.trigger.split(' ') : [];
17238         Roo.each(triggers, function(trigger) {
17239         
17240             if (trigger == 'click') {
17241                 on_el.on('click', this.toggle, this);
17242             } else if (trigger != 'manual') {
17243                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17244                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17245       
17246                 on_el.on(eventIn  ,this.enter, this);
17247                 on_el.on(eventOut, this.leave, this);
17248             }
17249         }, this);
17250         
17251     },
17252     
17253     
17254     // private
17255     timeout : null,
17256     hoverState : null,
17257     
17258     toggle : function () {
17259         this.hoverState == 'in' ? this.leave() : this.enter();
17260     },
17261     
17262     enter : function () {
17263         
17264         clearTimeout(this.timeout);
17265     
17266         this.hoverState = 'in';
17267     
17268         if (!this.delay || !this.delay.show) {
17269             this.show();
17270             return;
17271         }
17272         var _t = this;
17273         this.timeout = setTimeout(function () {
17274             if (_t.hoverState == 'in') {
17275                 _t.show();
17276             }
17277         }, this.delay.show)
17278     },
17279     
17280     leave : function() {
17281         clearTimeout(this.timeout);
17282     
17283         this.hoverState = 'out';
17284     
17285         if (!this.delay || !this.delay.hide) {
17286             this.hide();
17287             return;
17288         }
17289         var _t = this;
17290         this.timeout = setTimeout(function () {
17291             if (_t.hoverState == 'out') {
17292                 _t.hide();
17293             }
17294         }, this.delay.hide)
17295     },
17296     
17297     show : function (on_el)
17298     {
17299         if (!on_el) {
17300             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17301         }
17302         
17303         // set content.
17304         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17305         if (this.html !== false) {
17306             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17307         }
17308         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17309         if (!this.title.length) {
17310             this.el.select('.popover-title',true).hide();
17311         }
17312         
17313         var placement = typeof this.placement == 'function' ?
17314             this.placement.call(this, this.el, on_el) :
17315             this.placement;
17316             
17317         var autoToken = /\s?auto?\s?/i;
17318         var autoPlace = autoToken.test(placement);
17319         if (autoPlace) {
17320             placement = placement.replace(autoToken, '') || 'top';
17321         }
17322         
17323         //this.el.detach()
17324         //this.el.setXY([0,0]);
17325         this.el.show();
17326         this.el.dom.style.display='block';
17327         this.el.addClass(placement);
17328         
17329         //this.el.appendTo(on_el);
17330         
17331         var p = this.getPosition();
17332         var box = this.el.getBox();
17333         
17334         if (autoPlace) {
17335             // fixme..
17336         }
17337         var align = Roo.bootstrap.Popover.alignment[placement];
17338         
17339 //        Roo.log(align);
17340         this.el.alignTo(on_el, align[0],align[1]);
17341         //var arrow = this.el.select('.arrow',true).first();
17342         //arrow.set(align[2], 
17343         
17344         this.el.addClass('in');
17345         
17346         
17347         if (this.el.hasClass('fade')) {
17348             // fade it?
17349         }
17350         
17351         this.hoverState = 'in';
17352         
17353         this.fireEvent('show', this);
17354         
17355     },
17356     hide : function()
17357     {
17358         this.el.setXY([0,0]);
17359         this.el.removeClass('in');
17360         this.el.hide();
17361         this.hoverState = null;
17362         
17363         this.fireEvent('hide', this);
17364     }
17365     
17366 });
17367
17368 Roo.bootstrap.Popover.alignment = {
17369     'left' : ['r-l', [-10,0], 'right'],
17370     'right' : ['l-r', [10,0], 'left'],
17371     'bottom' : ['t-b', [0,10], 'top'],
17372     'top' : [ 'b-t', [0,-10], 'bottom']
17373 };
17374
17375  /*
17376  * - LGPL
17377  *
17378  * Progress
17379  * 
17380  */
17381
17382 /**
17383  * @class Roo.bootstrap.Progress
17384  * @extends Roo.bootstrap.Component
17385  * Bootstrap Progress class
17386  * @cfg {Boolean} striped striped of the progress bar
17387  * @cfg {Boolean} active animated of the progress bar
17388  * 
17389  * 
17390  * @constructor
17391  * Create a new Progress
17392  * @param {Object} config The config object
17393  */
17394
17395 Roo.bootstrap.Progress = function(config){
17396     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17397 };
17398
17399 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17400     
17401     striped : false,
17402     active: false,
17403     
17404     getAutoCreate : function(){
17405         var cfg = {
17406             tag: 'div',
17407             cls: 'progress'
17408         };
17409         
17410         
17411         if(this.striped){
17412             cfg.cls += ' progress-striped';
17413         }
17414       
17415         if(this.active){
17416             cfg.cls += ' active';
17417         }
17418         
17419         
17420         return cfg;
17421     }
17422    
17423 });
17424
17425  
17426
17427  /*
17428  * - LGPL
17429  *
17430  * ProgressBar
17431  * 
17432  */
17433
17434 /**
17435  * @class Roo.bootstrap.ProgressBar
17436  * @extends Roo.bootstrap.Component
17437  * Bootstrap ProgressBar class
17438  * @cfg {Number} aria_valuenow aria-value now
17439  * @cfg {Number} aria_valuemin aria-value min
17440  * @cfg {Number} aria_valuemax aria-value max
17441  * @cfg {String} label label for the progress bar
17442  * @cfg {String} panel (success | info | warning | danger )
17443  * @cfg {String} role role of the progress bar
17444  * @cfg {String} sr_only text
17445  * 
17446  * 
17447  * @constructor
17448  * Create a new ProgressBar
17449  * @param {Object} config The config object
17450  */
17451
17452 Roo.bootstrap.ProgressBar = function(config){
17453     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17454 };
17455
17456 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17457     
17458     aria_valuenow : 0,
17459     aria_valuemin : 0,
17460     aria_valuemax : 100,
17461     label : false,
17462     panel : false,
17463     role : false,
17464     sr_only: false,
17465     
17466     getAutoCreate : function()
17467     {
17468         
17469         var cfg = {
17470             tag: 'div',
17471             cls: 'progress-bar',
17472             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17473         };
17474         
17475         if(this.sr_only){
17476             cfg.cn = {
17477                 tag: 'span',
17478                 cls: 'sr-only',
17479                 html: this.sr_only
17480             }
17481         }
17482         
17483         if(this.role){
17484             cfg.role = this.role;
17485         }
17486         
17487         if(this.aria_valuenow){
17488             cfg['aria-valuenow'] = this.aria_valuenow;
17489         }
17490         
17491         if(this.aria_valuemin){
17492             cfg['aria-valuemin'] = this.aria_valuemin;
17493         }
17494         
17495         if(this.aria_valuemax){
17496             cfg['aria-valuemax'] = this.aria_valuemax;
17497         }
17498         
17499         if(this.label && !this.sr_only){
17500             cfg.html = this.label;
17501         }
17502         
17503         if(this.panel){
17504             cfg.cls += ' progress-bar-' + this.panel;
17505         }
17506         
17507         return cfg;
17508     },
17509     
17510     update : function(aria_valuenow)
17511     {
17512         this.aria_valuenow = aria_valuenow;
17513         
17514         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17515     }
17516    
17517 });
17518
17519  
17520
17521  /*
17522  * - LGPL
17523  *
17524  * column
17525  * 
17526  */
17527
17528 /**
17529  * @class Roo.bootstrap.TabGroup
17530  * @extends Roo.bootstrap.Column
17531  * Bootstrap Column class
17532  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17533  * @cfg {Boolean} carousel true to make the group behave like a carousel
17534  * @cfg {Boolean} bullets show bullets for the panels
17535  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17536  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17537  * @cfg {Boolean} showarrow (true|false) show arrow default true
17538  * 
17539  * @constructor
17540  * Create a new TabGroup
17541  * @param {Object} config The config object
17542  */
17543
17544 Roo.bootstrap.TabGroup = function(config){
17545     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17546     if (!this.navId) {
17547         this.navId = Roo.id();
17548     }
17549     this.tabs = [];
17550     Roo.bootstrap.TabGroup.register(this);
17551     
17552 };
17553
17554 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17555     
17556     carousel : false,
17557     transition : false,
17558     bullets : 0,
17559     timer : 0,
17560     autoslide : false,
17561     slideFn : false,
17562     slideOnTouch : false,
17563     showarrow : true,
17564     
17565     getAutoCreate : function()
17566     {
17567         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17568         
17569         cfg.cls += ' tab-content';
17570         
17571         if (this.carousel) {
17572             cfg.cls += ' carousel slide';
17573             
17574             cfg.cn = [{
17575                cls : 'carousel-inner',
17576                cn : []
17577             }];
17578         
17579             if(this.bullets  && !Roo.isTouch){
17580                 
17581                 var bullets = {
17582                     cls : 'carousel-bullets',
17583                     cn : []
17584                 };
17585                
17586                 if(this.bullets_cls){
17587                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17588                 }
17589                 
17590                 bullets.cn.push({
17591                     cls : 'clear'
17592                 });
17593                 
17594                 cfg.cn[0].cn.push(bullets);
17595             }
17596             
17597             if(this.showarrow){
17598                 cfg.cn[0].cn.push({
17599                     tag : 'div',
17600                     class : 'carousel-arrow',
17601                     cn : [
17602                         {
17603                             tag : 'div',
17604                             class : 'carousel-prev',
17605                             cn : [
17606                                 {
17607                                     tag : 'i',
17608                                     class : 'fa fa-chevron-left'
17609                                 }
17610                             ]
17611                         },
17612                         {
17613                             tag : 'div',
17614                             class : 'carousel-next',
17615                             cn : [
17616                                 {
17617                                     tag : 'i',
17618                                     class : 'fa fa-chevron-right'
17619                                 }
17620                             ]
17621                         }
17622                     ]
17623                 });
17624             }
17625             
17626         }
17627         
17628         return cfg;
17629     },
17630     
17631     initEvents:  function()
17632     {
17633 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17634 //            this.el.on("touchstart", this.onTouchStart, this);
17635 //        }
17636         
17637         if(this.autoslide){
17638             var _this = this;
17639             
17640             this.slideFn = window.setInterval(function() {
17641                 _this.showPanelNext();
17642             }, this.timer);
17643         }
17644         
17645         if(this.showarrow){
17646             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17647             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17648         }
17649         
17650         
17651     },
17652     
17653 //    onTouchStart : function(e, el, o)
17654 //    {
17655 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17656 //            return;
17657 //        }
17658 //        
17659 //        this.showPanelNext();
17660 //    },
17661     
17662     
17663     getChildContainer : function()
17664     {
17665         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17666     },
17667     
17668     /**
17669     * register a Navigation item
17670     * @param {Roo.bootstrap.NavItem} the navitem to add
17671     */
17672     register : function(item)
17673     {
17674         this.tabs.push( item);
17675         item.navId = this.navId; // not really needed..
17676         this.addBullet();
17677     
17678     },
17679     
17680     getActivePanel : function()
17681     {
17682         var r = false;
17683         Roo.each(this.tabs, function(t) {
17684             if (t.active) {
17685                 r = t;
17686                 return false;
17687             }
17688             return null;
17689         });
17690         return r;
17691         
17692     },
17693     getPanelByName : function(n)
17694     {
17695         var r = false;
17696         Roo.each(this.tabs, function(t) {
17697             if (t.tabId == n) {
17698                 r = t;
17699                 return false;
17700             }
17701             return null;
17702         });
17703         return r;
17704     },
17705     indexOfPanel : function(p)
17706     {
17707         var r = false;
17708         Roo.each(this.tabs, function(t,i) {
17709             if (t.tabId == p.tabId) {
17710                 r = i;
17711                 return false;
17712             }
17713             return null;
17714         });
17715         return r;
17716     },
17717     /**
17718      * show a specific panel
17719      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17720      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17721      */
17722     showPanel : function (pan)
17723     {
17724         if(this.transition || typeof(pan) == 'undefined'){
17725             Roo.log("waiting for the transitionend");
17726             return;
17727         }
17728         
17729         if (typeof(pan) == 'number') {
17730             pan = this.tabs[pan];
17731         }
17732         
17733         if (typeof(pan) == 'string') {
17734             pan = this.getPanelByName(pan);
17735         }
17736         
17737         var cur = this.getActivePanel();
17738         
17739         if(!pan || !cur){
17740             Roo.log('pan or acitve pan is undefined');
17741             return false;
17742         }
17743         
17744         if (pan.tabId == this.getActivePanel().tabId) {
17745             return true;
17746         }
17747         
17748         if (false === cur.fireEvent('beforedeactivate')) {
17749             return false;
17750         }
17751         
17752         if(this.bullets > 0 && !Roo.isTouch){
17753             this.setActiveBullet(this.indexOfPanel(pan));
17754         }
17755         
17756         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17757             
17758             this.transition = true;
17759             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17760             var lr = dir == 'next' ? 'left' : 'right';
17761             pan.el.addClass(dir); // or prev
17762             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17763             cur.el.addClass(lr); // or right
17764             pan.el.addClass(lr);
17765             
17766             var _this = this;
17767             cur.el.on('transitionend', function() {
17768                 Roo.log("trans end?");
17769                 
17770                 pan.el.removeClass([lr,dir]);
17771                 pan.setActive(true);
17772                 
17773                 cur.el.removeClass([lr]);
17774                 cur.setActive(false);
17775                 
17776                 _this.transition = false;
17777                 
17778             }, this, { single:  true } );
17779             
17780             return true;
17781         }
17782         
17783         cur.setActive(false);
17784         pan.setActive(true);
17785         
17786         return true;
17787         
17788     },
17789     showPanelNext : function()
17790     {
17791         var i = this.indexOfPanel(this.getActivePanel());
17792         
17793         if (i >= this.tabs.length - 1 && !this.autoslide) {
17794             return;
17795         }
17796         
17797         if (i >= this.tabs.length - 1 && this.autoslide) {
17798             i = -1;
17799         }
17800         
17801         this.showPanel(this.tabs[i+1]);
17802     },
17803     
17804     showPanelPrev : function()
17805     {
17806         var i = this.indexOfPanel(this.getActivePanel());
17807         
17808         if (i  < 1 && !this.autoslide) {
17809             return;
17810         }
17811         
17812         if (i < 1 && this.autoslide) {
17813             i = this.tabs.length;
17814         }
17815         
17816         this.showPanel(this.tabs[i-1]);
17817     },
17818     
17819     
17820     addBullet: function()
17821     {
17822         if(!this.bullets || Roo.isTouch){
17823             return;
17824         }
17825         var ctr = this.el.select('.carousel-bullets',true).first();
17826         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17827         var bullet = ctr.createChild({
17828             cls : 'bullet bullet-' + i
17829         },ctr.dom.lastChild);
17830         
17831         
17832         var _this = this;
17833         
17834         bullet.on('click', (function(e, el, o, ii, t){
17835
17836             e.preventDefault();
17837
17838             this.showPanel(ii);
17839
17840             if(this.autoslide && this.slideFn){
17841                 clearInterval(this.slideFn);
17842                 this.slideFn = window.setInterval(function() {
17843                     _this.showPanelNext();
17844                 }, this.timer);
17845             }
17846
17847         }).createDelegate(this, [i, bullet], true));
17848                 
17849         
17850     },
17851      
17852     setActiveBullet : function(i)
17853     {
17854         if(Roo.isTouch){
17855             return;
17856         }
17857         
17858         Roo.each(this.el.select('.bullet', true).elements, function(el){
17859             el.removeClass('selected');
17860         });
17861
17862         var bullet = this.el.select('.bullet-' + i, true).first();
17863         
17864         if(!bullet){
17865             return;
17866         }
17867         
17868         bullet.addClass('selected');
17869     }
17870     
17871     
17872   
17873 });
17874
17875  
17876
17877  
17878  
17879 Roo.apply(Roo.bootstrap.TabGroup, {
17880     
17881     groups: {},
17882      /**
17883     * register a Navigation Group
17884     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17885     */
17886     register : function(navgrp)
17887     {
17888         this.groups[navgrp.navId] = navgrp;
17889         
17890     },
17891     /**
17892     * fetch a Navigation Group based on the navigation ID
17893     * if one does not exist , it will get created.
17894     * @param {string} the navgroup to add
17895     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17896     */
17897     get: function(navId) {
17898         if (typeof(this.groups[navId]) == 'undefined') {
17899             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17900         }
17901         return this.groups[navId] ;
17902     }
17903     
17904     
17905     
17906 });
17907
17908  /*
17909  * - LGPL
17910  *
17911  * TabPanel
17912  * 
17913  */
17914
17915 /**
17916  * @class Roo.bootstrap.TabPanel
17917  * @extends Roo.bootstrap.Component
17918  * Bootstrap TabPanel class
17919  * @cfg {Boolean} active panel active
17920  * @cfg {String} html panel content
17921  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17922  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17923  * @cfg {String} href click to link..
17924  * 
17925  * 
17926  * @constructor
17927  * Create a new TabPanel
17928  * @param {Object} config The config object
17929  */
17930
17931 Roo.bootstrap.TabPanel = function(config){
17932     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17933     this.addEvents({
17934         /**
17935              * @event changed
17936              * Fires when the active status changes
17937              * @param {Roo.bootstrap.TabPanel} this
17938              * @param {Boolean} state the new state
17939             
17940          */
17941         'changed': true,
17942         /**
17943              * @event beforedeactivate
17944              * Fires before a tab is de-activated - can be used to do validation on a form.
17945              * @param {Roo.bootstrap.TabPanel} this
17946              * @return {Boolean} false if there is an error
17947             
17948          */
17949         'beforedeactivate': true
17950      });
17951     
17952     this.tabId = this.tabId || Roo.id();
17953   
17954 };
17955
17956 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17957     
17958     active: false,
17959     html: false,
17960     tabId: false,
17961     navId : false,
17962     href : '',
17963     
17964     getAutoCreate : function(){
17965         var cfg = {
17966             tag: 'div',
17967             // item is needed for carousel - not sure if it has any effect otherwise
17968             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17969             html: this.html || ''
17970         };
17971         
17972         if(this.active){
17973             cfg.cls += ' active';
17974         }
17975         
17976         if(this.tabId){
17977             cfg.tabId = this.tabId;
17978         }
17979         
17980         
17981         return cfg;
17982     },
17983     
17984     initEvents:  function()
17985     {
17986         var p = this.parent();
17987         
17988         this.navId = this.navId || p.navId;
17989         
17990         if (typeof(this.navId) != 'undefined') {
17991             // not really needed.. but just in case.. parent should be a NavGroup.
17992             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17993             
17994             tg.register(this);
17995             
17996             var i = tg.tabs.length - 1;
17997             
17998             if(this.active && tg.bullets > 0 && i < tg.bullets){
17999                 tg.setActiveBullet(i);
18000             }
18001         }
18002         
18003         this.el.on('click', this.onClick, this);
18004         
18005         if(Roo.isTouch){
18006             this.el.on("touchstart", this.onTouchStart, this);
18007             this.el.on("touchmove", this.onTouchMove, this);
18008             this.el.on("touchend", this.onTouchEnd, this);
18009         }
18010         
18011     },
18012     
18013     onRender : function(ct, position)
18014     {
18015         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18016     },
18017     
18018     setActive : function(state)
18019     {
18020         Roo.log("panel - set active " + this.tabId + "=" + state);
18021         
18022         this.active = state;
18023         if (!state) {
18024             this.el.removeClass('active');
18025             
18026         } else  if (!this.el.hasClass('active')) {
18027             this.el.addClass('active');
18028         }
18029         
18030         this.fireEvent('changed', this, state);
18031     },
18032     
18033     onClick : function(e)
18034     {
18035         e.preventDefault();
18036         
18037         if(!this.href.length){
18038             return;
18039         }
18040         
18041         window.location.href = this.href;
18042     },
18043     
18044     startX : 0,
18045     startY : 0,
18046     endX : 0,
18047     endY : 0,
18048     swiping : false,
18049     
18050     onTouchStart : function(e)
18051     {
18052         this.swiping = false;
18053         
18054         this.startX = e.browserEvent.touches[0].clientX;
18055         this.startY = e.browserEvent.touches[0].clientY;
18056     },
18057     
18058     onTouchMove : function(e)
18059     {
18060         this.swiping = true;
18061         
18062         this.endX = e.browserEvent.touches[0].clientX;
18063         this.endY = e.browserEvent.touches[0].clientY;
18064     },
18065     
18066     onTouchEnd : function(e)
18067     {
18068         if(!this.swiping){
18069             this.onClick(e);
18070             return;
18071         }
18072         
18073         var tabGroup = this.parent();
18074         
18075         if(this.endX > this.startX){ // swiping right
18076             tabGroup.showPanelPrev();
18077             return;
18078         }
18079         
18080         if(this.startX > this.endX){ // swiping left
18081             tabGroup.showPanelNext();
18082             return;
18083         }
18084     }
18085     
18086     
18087 });
18088  
18089
18090  
18091
18092  /*
18093  * - LGPL
18094  *
18095  * DateField
18096  * 
18097  */
18098
18099 /**
18100  * @class Roo.bootstrap.DateField
18101  * @extends Roo.bootstrap.Input
18102  * Bootstrap DateField class
18103  * @cfg {Number} weekStart default 0
18104  * @cfg {String} viewMode default empty, (months|years)
18105  * @cfg {String} minViewMode default empty, (months|years)
18106  * @cfg {Number} startDate default -Infinity
18107  * @cfg {Number} endDate default Infinity
18108  * @cfg {Boolean} todayHighlight default false
18109  * @cfg {Boolean} todayBtn default false
18110  * @cfg {Boolean} calendarWeeks default false
18111  * @cfg {Object} daysOfWeekDisabled default empty
18112  * @cfg {Boolean} singleMode default false (true | false)
18113  * 
18114  * @cfg {Boolean} keyboardNavigation default true
18115  * @cfg {String} language default en
18116  * 
18117  * @constructor
18118  * Create a new DateField
18119  * @param {Object} config The config object
18120  */
18121
18122 Roo.bootstrap.DateField = function(config){
18123     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18124      this.addEvents({
18125             /**
18126              * @event show
18127              * Fires when this field show.
18128              * @param {Roo.bootstrap.DateField} this
18129              * @param {Mixed} date The date value
18130              */
18131             show : true,
18132             /**
18133              * @event show
18134              * Fires when this field hide.
18135              * @param {Roo.bootstrap.DateField} this
18136              * @param {Mixed} date The date value
18137              */
18138             hide : true,
18139             /**
18140              * @event select
18141              * Fires when select a date.
18142              * @param {Roo.bootstrap.DateField} this
18143              * @param {Mixed} date The date value
18144              */
18145             select : true,
18146             /**
18147              * @event beforeselect
18148              * Fires when before select a date.
18149              * @param {Roo.bootstrap.DateField} this
18150              * @param {Mixed} date The date value
18151              */
18152             beforeselect : true
18153         });
18154 };
18155
18156 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18157     
18158     /**
18159      * @cfg {String} format
18160      * The default date format string which can be overriden for localization support.  The format must be
18161      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18162      */
18163     format : "m/d/y",
18164     /**
18165      * @cfg {String} altFormats
18166      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18167      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18168      */
18169     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18170     
18171     weekStart : 0,
18172     
18173     viewMode : '',
18174     
18175     minViewMode : '',
18176     
18177     todayHighlight : false,
18178     
18179     todayBtn: false,
18180     
18181     language: 'en',
18182     
18183     keyboardNavigation: true,
18184     
18185     calendarWeeks: false,
18186     
18187     startDate: -Infinity,
18188     
18189     endDate: Infinity,
18190     
18191     daysOfWeekDisabled: [],
18192     
18193     _events: [],
18194     
18195     singleMode : false,
18196     
18197     UTCDate: function()
18198     {
18199         return new Date(Date.UTC.apply(Date, arguments));
18200     },
18201     
18202     UTCToday: function()
18203     {
18204         var today = new Date();
18205         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18206     },
18207     
18208     getDate: function() {
18209             var d = this.getUTCDate();
18210             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18211     },
18212     
18213     getUTCDate: function() {
18214             return this.date;
18215     },
18216     
18217     setDate: function(d) {
18218             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18219     },
18220     
18221     setUTCDate: function(d) {
18222             this.date = d;
18223             this.setValue(this.formatDate(this.date));
18224     },
18225         
18226     onRender: function(ct, position)
18227     {
18228         
18229         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18230         
18231         this.language = this.language || 'en';
18232         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18233         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18234         
18235         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18236         this.format = this.format || 'm/d/y';
18237         this.isInline = false;
18238         this.isInput = true;
18239         this.component = this.el.select('.add-on', true).first() || false;
18240         this.component = (this.component && this.component.length === 0) ? false : this.component;
18241         this.hasInput = this.component && this.inputEl().length;
18242         
18243         if (typeof(this.minViewMode === 'string')) {
18244             switch (this.minViewMode) {
18245                 case 'months':
18246                     this.minViewMode = 1;
18247                     break;
18248                 case 'years':
18249                     this.minViewMode = 2;
18250                     break;
18251                 default:
18252                     this.minViewMode = 0;
18253                     break;
18254             }
18255         }
18256         
18257         if (typeof(this.viewMode === 'string')) {
18258             switch (this.viewMode) {
18259                 case 'months':
18260                     this.viewMode = 1;
18261                     break;
18262                 case 'years':
18263                     this.viewMode = 2;
18264                     break;
18265                 default:
18266                     this.viewMode = 0;
18267                     break;
18268             }
18269         }
18270                 
18271         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18272         
18273 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18274         
18275         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18276         
18277         this.picker().on('mousedown', this.onMousedown, this);
18278         this.picker().on('click', this.onClick, this);
18279         
18280         this.picker().addClass('datepicker-dropdown');
18281         
18282         this.startViewMode = this.viewMode;
18283         
18284         if(this.singleMode){
18285             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18286                 v.setVisibilityMode(Roo.Element.DISPLAY);
18287                 v.hide();
18288             });
18289             
18290             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18291                 v.setStyle('width', '189px');
18292             });
18293         }
18294         
18295         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18296             if(!this.calendarWeeks){
18297                 v.remove();
18298                 return;
18299             }
18300             
18301             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18302             v.attr('colspan', function(i, val){
18303                 return parseInt(val) + 1;
18304             });
18305         });
18306                         
18307         
18308         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18309         
18310         this.setStartDate(this.startDate);
18311         this.setEndDate(this.endDate);
18312         
18313         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18314         
18315         this.fillDow();
18316         this.fillMonths();
18317         this.update();
18318         this.showMode();
18319         
18320         if(this.isInline) {
18321             this.show();
18322         }
18323     },
18324     
18325     picker : function()
18326     {
18327         return this.pickerEl;
18328 //        return this.el.select('.datepicker', true).first();
18329     },
18330     
18331     fillDow: function()
18332     {
18333         var dowCnt = this.weekStart;
18334         
18335         var dow = {
18336             tag: 'tr',
18337             cn: [
18338                 
18339             ]
18340         };
18341         
18342         if(this.calendarWeeks){
18343             dow.cn.push({
18344                 tag: 'th',
18345                 cls: 'cw',
18346                 html: '&nbsp;'
18347             })
18348         }
18349         
18350         while (dowCnt < this.weekStart + 7) {
18351             dow.cn.push({
18352                 tag: 'th',
18353                 cls: 'dow',
18354                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18355             });
18356         }
18357         
18358         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18359     },
18360     
18361     fillMonths: function()
18362     {    
18363         var i = 0;
18364         var months = this.picker().select('>.datepicker-months td', true).first();
18365         
18366         months.dom.innerHTML = '';
18367         
18368         while (i < 12) {
18369             var month = {
18370                 tag: 'span',
18371                 cls: 'month',
18372                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18373             };
18374             
18375             months.createChild(month);
18376         }
18377         
18378     },
18379     
18380     update: function()
18381     {
18382         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;
18383         
18384         if (this.date < this.startDate) {
18385             this.viewDate = new Date(this.startDate);
18386         } else if (this.date > this.endDate) {
18387             this.viewDate = new Date(this.endDate);
18388         } else {
18389             this.viewDate = new Date(this.date);
18390         }
18391         
18392         this.fill();
18393     },
18394     
18395     fill: function() 
18396     {
18397         var d = new Date(this.viewDate),
18398                 year = d.getUTCFullYear(),
18399                 month = d.getUTCMonth(),
18400                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18401                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18402                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18403                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18404                 currentDate = this.date && this.date.valueOf(),
18405                 today = this.UTCToday();
18406         
18407         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18408         
18409 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18410         
18411 //        this.picker.select('>tfoot th.today').
18412 //                                              .text(dates[this.language].today)
18413 //                                              .toggle(this.todayBtn !== false);
18414     
18415         this.updateNavArrows();
18416         this.fillMonths();
18417                                                 
18418         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18419         
18420         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18421          
18422         prevMonth.setUTCDate(day);
18423         
18424         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18425         
18426         var nextMonth = new Date(prevMonth);
18427         
18428         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18429         
18430         nextMonth = nextMonth.valueOf();
18431         
18432         var fillMonths = false;
18433         
18434         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18435         
18436         while(prevMonth.valueOf() < nextMonth) {
18437             var clsName = '';
18438             
18439             if (prevMonth.getUTCDay() === this.weekStart) {
18440                 if(fillMonths){
18441                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18442                 }
18443                     
18444                 fillMonths = {
18445                     tag: 'tr',
18446                     cn: []
18447                 };
18448                 
18449                 if(this.calendarWeeks){
18450                     // ISO 8601: First week contains first thursday.
18451                     // ISO also states week starts on Monday, but we can be more abstract here.
18452                     var
18453                     // Start of current week: based on weekstart/current date
18454                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18455                     // Thursday of this week
18456                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18457                     // First Thursday of year, year from thursday
18458                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18459                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18460                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18461                     
18462                     fillMonths.cn.push({
18463                         tag: 'td',
18464                         cls: 'cw',
18465                         html: calWeek
18466                     });
18467                 }
18468             }
18469             
18470             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18471                 clsName += ' old';
18472             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18473                 clsName += ' new';
18474             }
18475             if (this.todayHighlight &&
18476                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18477                 prevMonth.getUTCMonth() == today.getMonth() &&
18478                 prevMonth.getUTCDate() == today.getDate()) {
18479                 clsName += ' today';
18480             }
18481             
18482             if (currentDate && prevMonth.valueOf() === currentDate) {
18483                 clsName += ' active';
18484             }
18485             
18486             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18487                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18488                     clsName += ' disabled';
18489             }
18490             
18491             fillMonths.cn.push({
18492                 tag: 'td',
18493                 cls: 'day ' + clsName,
18494                 html: prevMonth.getDate()
18495             });
18496             
18497             prevMonth.setDate(prevMonth.getDate()+1);
18498         }
18499           
18500         var currentYear = this.date && this.date.getUTCFullYear();
18501         var currentMonth = this.date && this.date.getUTCMonth();
18502         
18503         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18504         
18505         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18506             v.removeClass('active');
18507             
18508             if(currentYear === year && k === currentMonth){
18509                 v.addClass('active');
18510             }
18511             
18512             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18513                 v.addClass('disabled');
18514             }
18515             
18516         });
18517         
18518         
18519         year = parseInt(year/10, 10) * 10;
18520         
18521         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18522         
18523         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18524         
18525         year -= 1;
18526         for (var i = -1; i < 11; i++) {
18527             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18528                 tag: 'span',
18529                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18530                 html: year
18531             });
18532             
18533             year += 1;
18534         }
18535     },
18536     
18537     showMode: function(dir) 
18538     {
18539         if (dir) {
18540             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18541         }
18542         
18543         Roo.each(this.picker().select('>div',true).elements, function(v){
18544             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18545             v.hide();
18546         });
18547         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18548     },
18549     
18550     place: function()
18551     {
18552         if(this.isInline) {
18553             return;
18554         }
18555         
18556         this.picker().removeClass(['bottom', 'top']);
18557         
18558         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18559             /*
18560              * place to the top of element!
18561              *
18562              */
18563             
18564             this.picker().addClass('top');
18565             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18566             
18567             return;
18568         }
18569         
18570         this.picker().addClass('bottom');
18571         
18572         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18573     },
18574     
18575     parseDate : function(value)
18576     {
18577         if(!value || value instanceof Date){
18578             return value;
18579         }
18580         var v = Date.parseDate(value, this.format);
18581         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18582             v = Date.parseDate(value, 'Y-m-d');
18583         }
18584         if(!v && this.altFormats){
18585             if(!this.altFormatsArray){
18586                 this.altFormatsArray = this.altFormats.split("|");
18587             }
18588             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18589                 v = Date.parseDate(value, this.altFormatsArray[i]);
18590             }
18591         }
18592         return v;
18593     },
18594     
18595     formatDate : function(date, fmt)
18596     {   
18597         return (!date || !(date instanceof Date)) ?
18598         date : date.dateFormat(fmt || this.format);
18599     },
18600     
18601     onFocus : function()
18602     {
18603         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18604         this.show();
18605     },
18606     
18607     onBlur : function()
18608     {
18609         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18610         
18611         var d = this.inputEl().getValue();
18612         
18613         this.setValue(d);
18614                 
18615         this.hide();
18616     },
18617     
18618     show : function()
18619     {
18620         this.picker().show();
18621         this.update();
18622         this.place();
18623         
18624         this.fireEvent('show', this, this.date);
18625     },
18626     
18627     hide : function()
18628     {
18629         if(this.isInline) {
18630             return;
18631         }
18632         this.picker().hide();
18633         this.viewMode = this.startViewMode;
18634         this.showMode();
18635         
18636         this.fireEvent('hide', this, this.date);
18637         
18638     },
18639     
18640     onMousedown: function(e)
18641     {
18642         e.stopPropagation();
18643         e.preventDefault();
18644     },
18645     
18646     keyup: function(e)
18647     {
18648         Roo.bootstrap.DateField.superclass.keyup.call(this);
18649         this.update();
18650     },
18651
18652     setValue: function(v)
18653     {
18654         if(this.fireEvent('beforeselect', this, v) !== false){
18655             var d = new Date(this.parseDate(v) ).clearTime();
18656         
18657             if(isNaN(d.getTime())){
18658                 this.date = this.viewDate = '';
18659                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18660                 return;
18661             }
18662
18663             v = this.formatDate(d);
18664
18665             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18666
18667             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18668
18669             this.update();
18670
18671             this.fireEvent('select', this, this.date);
18672         }
18673     },
18674     
18675     getValue: function()
18676     {
18677         return this.formatDate(this.date);
18678     },
18679     
18680     fireKey: function(e)
18681     {
18682         if (!this.picker().isVisible()){
18683             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18684                 this.show();
18685             }
18686             return;
18687         }
18688         
18689         var dateChanged = false,
18690         dir, day, month,
18691         newDate, newViewDate;
18692         
18693         switch(e.keyCode){
18694             case 27: // escape
18695                 this.hide();
18696                 e.preventDefault();
18697                 break;
18698             case 37: // left
18699             case 39: // right
18700                 if (!this.keyboardNavigation) {
18701                     break;
18702                 }
18703                 dir = e.keyCode == 37 ? -1 : 1;
18704                 
18705                 if (e.ctrlKey){
18706                     newDate = this.moveYear(this.date, dir);
18707                     newViewDate = this.moveYear(this.viewDate, dir);
18708                 } else if (e.shiftKey){
18709                     newDate = this.moveMonth(this.date, dir);
18710                     newViewDate = this.moveMonth(this.viewDate, dir);
18711                 } else {
18712                     newDate = new Date(this.date);
18713                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18714                     newViewDate = new Date(this.viewDate);
18715                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18716                 }
18717                 if (this.dateWithinRange(newDate)){
18718                     this.date = newDate;
18719                     this.viewDate = newViewDate;
18720                     this.setValue(this.formatDate(this.date));
18721 //                    this.update();
18722                     e.preventDefault();
18723                     dateChanged = true;
18724                 }
18725                 break;
18726             case 38: // up
18727             case 40: // down
18728                 if (!this.keyboardNavigation) {
18729                     break;
18730                 }
18731                 dir = e.keyCode == 38 ? -1 : 1;
18732                 if (e.ctrlKey){
18733                     newDate = this.moveYear(this.date, dir);
18734                     newViewDate = this.moveYear(this.viewDate, dir);
18735                 } else if (e.shiftKey){
18736                     newDate = this.moveMonth(this.date, dir);
18737                     newViewDate = this.moveMonth(this.viewDate, dir);
18738                 } else {
18739                     newDate = new Date(this.date);
18740                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18741                     newViewDate = new Date(this.viewDate);
18742                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18743                 }
18744                 if (this.dateWithinRange(newDate)){
18745                     this.date = newDate;
18746                     this.viewDate = newViewDate;
18747                     this.setValue(this.formatDate(this.date));
18748 //                    this.update();
18749                     e.preventDefault();
18750                     dateChanged = true;
18751                 }
18752                 break;
18753             case 13: // enter
18754                 this.setValue(this.formatDate(this.date));
18755                 this.hide();
18756                 e.preventDefault();
18757                 break;
18758             case 9: // tab
18759                 this.setValue(this.formatDate(this.date));
18760                 this.hide();
18761                 break;
18762             case 16: // shift
18763             case 17: // ctrl
18764             case 18: // alt
18765                 break;
18766             default :
18767                 this.hide();
18768                 
18769         }
18770     },
18771     
18772     
18773     onClick: function(e) 
18774     {
18775         e.stopPropagation();
18776         e.preventDefault();
18777         
18778         var target = e.getTarget();
18779         
18780         if(target.nodeName.toLowerCase() === 'i'){
18781             target = Roo.get(target).dom.parentNode;
18782         }
18783         
18784         var nodeName = target.nodeName;
18785         var className = target.className;
18786         var html = target.innerHTML;
18787         //Roo.log(nodeName);
18788         
18789         switch(nodeName.toLowerCase()) {
18790             case 'th':
18791                 switch(className) {
18792                     case 'switch':
18793                         this.showMode(1);
18794                         break;
18795                     case 'prev':
18796                     case 'next':
18797                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18798                         switch(this.viewMode){
18799                                 case 0:
18800                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18801                                         break;
18802                                 case 1:
18803                                 case 2:
18804                                         this.viewDate = this.moveYear(this.viewDate, dir);
18805                                         break;
18806                         }
18807                         this.fill();
18808                         break;
18809                     case 'today':
18810                         var date = new Date();
18811                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18812 //                        this.fill()
18813                         this.setValue(this.formatDate(this.date));
18814                         
18815                         this.hide();
18816                         break;
18817                 }
18818                 break;
18819             case 'span':
18820                 if (className.indexOf('disabled') < 0) {
18821                     this.viewDate.setUTCDate(1);
18822                     if (className.indexOf('month') > -1) {
18823                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18824                     } else {
18825                         var year = parseInt(html, 10) || 0;
18826                         this.viewDate.setUTCFullYear(year);
18827                         
18828                     }
18829                     
18830                     if(this.singleMode){
18831                         this.setValue(this.formatDate(this.viewDate));
18832                         this.hide();
18833                         return;
18834                     }
18835                     
18836                     this.showMode(-1);
18837                     this.fill();
18838                 }
18839                 break;
18840                 
18841             case 'td':
18842                 //Roo.log(className);
18843                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18844                     var day = parseInt(html, 10) || 1;
18845                     var year = this.viewDate.getUTCFullYear(),
18846                         month = this.viewDate.getUTCMonth();
18847
18848                     if (className.indexOf('old') > -1) {
18849                         if(month === 0 ){
18850                             month = 11;
18851                             year -= 1;
18852                         }else{
18853                             month -= 1;
18854                         }
18855                     } else if (className.indexOf('new') > -1) {
18856                         if (month == 11) {
18857                             month = 0;
18858                             year += 1;
18859                         } else {
18860                             month += 1;
18861                         }
18862                     }
18863                     //Roo.log([year,month,day]);
18864                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18865                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18866 //                    this.fill();
18867                     //Roo.log(this.formatDate(this.date));
18868                     this.setValue(this.formatDate(this.date));
18869                     this.hide();
18870                 }
18871                 break;
18872         }
18873     },
18874     
18875     setStartDate: function(startDate)
18876     {
18877         this.startDate = startDate || -Infinity;
18878         if (this.startDate !== -Infinity) {
18879             this.startDate = this.parseDate(this.startDate);
18880         }
18881         this.update();
18882         this.updateNavArrows();
18883     },
18884
18885     setEndDate: function(endDate)
18886     {
18887         this.endDate = endDate || Infinity;
18888         if (this.endDate !== Infinity) {
18889             this.endDate = this.parseDate(this.endDate);
18890         }
18891         this.update();
18892         this.updateNavArrows();
18893     },
18894     
18895     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18896     {
18897         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18898         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18899             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18900         }
18901         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18902             return parseInt(d, 10);
18903         });
18904         this.update();
18905         this.updateNavArrows();
18906     },
18907     
18908     updateNavArrows: function() 
18909     {
18910         if(this.singleMode){
18911             return;
18912         }
18913         
18914         var d = new Date(this.viewDate),
18915         year = d.getUTCFullYear(),
18916         month = d.getUTCMonth();
18917         
18918         Roo.each(this.picker().select('.prev', true).elements, function(v){
18919             v.show();
18920             switch (this.viewMode) {
18921                 case 0:
18922
18923                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18924                         v.hide();
18925                     }
18926                     break;
18927                 case 1:
18928                 case 2:
18929                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18930                         v.hide();
18931                     }
18932                     break;
18933             }
18934         });
18935         
18936         Roo.each(this.picker().select('.next', true).elements, function(v){
18937             v.show();
18938             switch (this.viewMode) {
18939                 case 0:
18940
18941                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18942                         v.hide();
18943                     }
18944                     break;
18945                 case 1:
18946                 case 2:
18947                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18948                         v.hide();
18949                     }
18950                     break;
18951             }
18952         })
18953     },
18954     
18955     moveMonth: function(date, dir)
18956     {
18957         if (!dir) {
18958             return date;
18959         }
18960         var new_date = new Date(date.valueOf()),
18961         day = new_date.getUTCDate(),
18962         month = new_date.getUTCMonth(),
18963         mag = Math.abs(dir),
18964         new_month, test;
18965         dir = dir > 0 ? 1 : -1;
18966         if (mag == 1){
18967             test = dir == -1
18968             // If going back one month, make sure month is not current month
18969             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18970             ? function(){
18971                 return new_date.getUTCMonth() == month;
18972             }
18973             // If going forward one month, make sure month is as expected
18974             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18975             : function(){
18976                 return new_date.getUTCMonth() != new_month;
18977             };
18978             new_month = month + dir;
18979             new_date.setUTCMonth(new_month);
18980             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18981             if (new_month < 0 || new_month > 11) {
18982                 new_month = (new_month + 12) % 12;
18983             }
18984         } else {
18985             // For magnitudes >1, move one month at a time...
18986             for (var i=0; i<mag; i++) {
18987                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18988                 new_date = this.moveMonth(new_date, dir);
18989             }
18990             // ...then reset the day, keeping it in the new month
18991             new_month = new_date.getUTCMonth();
18992             new_date.setUTCDate(day);
18993             test = function(){
18994                 return new_month != new_date.getUTCMonth();
18995             };
18996         }
18997         // Common date-resetting loop -- if date is beyond end of month, make it
18998         // end of month
18999         while (test()){
19000             new_date.setUTCDate(--day);
19001             new_date.setUTCMonth(new_month);
19002         }
19003         return new_date;
19004     },
19005
19006     moveYear: function(date, dir)
19007     {
19008         return this.moveMonth(date, dir*12);
19009     },
19010
19011     dateWithinRange: function(date)
19012     {
19013         return date >= this.startDate && date <= this.endDate;
19014     },
19015
19016     
19017     remove: function() 
19018     {
19019         this.picker().remove();
19020     },
19021     
19022     validateValue : function(value)
19023     {
19024         if(value.length < 1)  {
19025             if(this.allowBlank){
19026                 return true;
19027             }
19028             return false;
19029         }
19030         
19031         if(value.length < this.minLength){
19032             return false;
19033         }
19034         if(value.length > this.maxLength){
19035             return false;
19036         }
19037         if(this.vtype){
19038             var vt = Roo.form.VTypes;
19039             if(!vt[this.vtype](value, this)){
19040                 return false;
19041             }
19042         }
19043         if(typeof this.validator == "function"){
19044             var msg = this.validator(value);
19045             if(msg !== true){
19046                 return false;
19047             }
19048         }
19049         
19050         if(this.regex && !this.regex.test(value)){
19051             return false;
19052         }
19053         
19054         if(typeof(this.parseDate(value)) == 'undefined'){
19055             return false;
19056         }
19057         
19058         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19059             return false;
19060         }      
19061         
19062         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19063             return false;
19064         } 
19065         
19066         
19067         return true;
19068     }
19069    
19070 });
19071
19072 Roo.apply(Roo.bootstrap.DateField,  {
19073     
19074     head : {
19075         tag: 'thead',
19076         cn: [
19077         {
19078             tag: 'tr',
19079             cn: [
19080             {
19081                 tag: 'th',
19082                 cls: 'prev',
19083                 html: '<i class="fa fa-arrow-left"/>'
19084             },
19085             {
19086                 tag: 'th',
19087                 cls: 'switch',
19088                 colspan: '5'
19089             },
19090             {
19091                 tag: 'th',
19092                 cls: 'next',
19093                 html: '<i class="fa fa-arrow-right"/>'
19094             }
19095
19096             ]
19097         }
19098         ]
19099     },
19100     
19101     content : {
19102         tag: 'tbody',
19103         cn: [
19104         {
19105             tag: 'tr',
19106             cn: [
19107             {
19108                 tag: 'td',
19109                 colspan: '7'
19110             }
19111             ]
19112         }
19113         ]
19114     },
19115     
19116     footer : {
19117         tag: 'tfoot',
19118         cn: [
19119         {
19120             tag: 'tr',
19121             cn: [
19122             {
19123                 tag: 'th',
19124                 colspan: '7',
19125                 cls: 'today'
19126             }
19127                     
19128             ]
19129         }
19130         ]
19131     },
19132     
19133     dates:{
19134         en: {
19135             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19136             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19137             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19138             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19139             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19140             today: "Today"
19141         }
19142     },
19143     
19144     modes: [
19145     {
19146         clsName: 'days',
19147         navFnc: 'Month',
19148         navStep: 1
19149     },
19150     {
19151         clsName: 'months',
19152         navFnc: 'FullYear',
19153         navStep: 1
19154     },
19155     {
19156         clsName: 'years',
19157         navFnc: 'FullYear',
19158         navStep: 10
19159     }]
19160 });
19161
19162 Roo.apply(Roo.bootstrap.DateField,  {
19163   
19164     template : {
19165         tag: 'div',
19166         cls: 'datepicker dropdown-menu roo-dynamic',
19167         cn: [
19168         {
19169             tag: 'div',
19170             cls: 'datepicker-days',
19171             cn: [
19172             {
19173                 tag: 'table',
19174                 cls: 'table-condensed',
19175                 cn:[
19176                 Roo.bootstrap.DateField.head,
19177                 {
19178                     tag: 'tbody'
19179                 },
19180                 Roo.bootstrap.DateField.footer
19181                 ]
19182             }
19183             ]
19184         },
19185         {
19186             tag: 'div',
19187             cls: 'datepicker-months',
19188             cn: [
19189             {
19190                 tag: 'table',
19191                 cls: 'table-condensed',
19192                 cn:[
19193                 Roo.bootstrap.DateField.head,
19194                 Roo.bootstrap.DateField.content,
19195                 Roo.bootstrap.DateField.footer
19196                 ]
19197             }
19198             ]
19199         },
19200         {
19201             tag: 'div',
19202             cls: 'datepicker-years',
19203             cn: [
19204             {
19205                 tag: 'table',
19206                 cls: 'table-condensed',
19207                 cn:[
19208                 Roo.bootstrap.DateField.head,
19209                 Roo.bootstrap.DateField.content,
19210                 Roo.bootstrap.DateField.footer
19211                 ]
19212             }
19213             ]
19214         }
19215         ]
19216     }
19217 });
19218
19219  
19220
19221  /*
19222  * - LGPL
19223  *
19224  * TimeField
19225  * 
19226  */
19227
19228 /**
19229  * @class Roo.bootstrap.TimeField
19230  * @extends Roo.bootstrap.Input
19231  * Bootstrap DateField class
19232  * 
19233  * 
19234  * @constructor
19235  * Create a new TimeField
19236  * @param {Object} config The config object
19237  */
19238
19239 Roo.bootstrap.TimeField = function(config){
19240     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19241     this.addEvents({
19242             /**
19243              * @event show
19244              * Fires when this field show.
19245              * @param {Roo.bootstrap.DateField} thisthis
19246              * @param {Mixed} date The date value
19247              */
19248             show : true,
19249             /**
19250              * @event show
19251              * Fires when this field hide.
19252              * @param {Roo.bootstrap.DateField} this
19253              * @param {Mixed} date The date value
19254              */
19255             hide : true,
19256             /**
19257              * @event select
19258              * Fires when select a date.
19259              * @param {Roo.bootstrap.DateField} this
19260              * @param {Mixed} date The date value
19261              */
19262             select : true
19263         });
19264 };
19265
19266 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19267     
19268     /**
19269      * @cfg {String} format
19270      * The default time format string which can be overriden for localization support.  The format must be
19271      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19272      */
19273     format : "H:i",
19274        
19275     onRender: function(ct, position)
19276     {
19277         
19278         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19279                 
19280         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19281         
19282         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19283         
19284         this.pop = this.picker().select('>.datepicker-time',true).first();
19285         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19286         
19287         this.picker().on('mousedown', this.onMousedown, this);
19288         this.picker().on('click', this.onClick, this);
19289         
19290         this.picker().addClass('datepicker-dropdown');
19291     
19292         this.fillTime();
19293         this.update();
19294             
19295         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19296         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19297         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19298         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19299         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19300         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19301
19302     },
19303     
19304     fireKey: function(e){
19305         if (!this.picker().isVisible()){
19306             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19307                 this.show();
19308             }
19309             return;
19310         }
19311
19312         e.preventDefault();
19313         
19314         switch(e.keyCode){
19315             case 27: // escape
19316                 this.hide();
19317                 break;
19318             case 37: // left
19319             case 39: // right
19320                 this.onTogglePeriod();
19321                 break;
19322             case 38: // up
19323                 this.onIncrementMinutes();
19324                 break;
19325             case 40: // down
19326                 this.onDecrementMinutes();
19327                 break;
19328             case 13: // enter
19329             case 9: // tab
19330                 this.setTime();
19331                 break;
19332         }
19333     },
19334     
19335     onClick: function(e) {
19336         e.stopPropagation();
19337         e.preventDefault();
19338     },
19339     
19340     picker : function()
19341     {
19342         return this.el.select('.datepicker', true).first();
19343     },
19344     
19345     fillTime: function()
19346     {    
19347         var time = this.pop.select('tbody', true).first();
19348         
19349         time.dom.innerHTML = '';
19350         
19351         time.createChild({
19352             tag: 'tr',
19353             cn: [
19354                 {
19355                     tag: 'td',
19356                     cn: [
19357                         {
19358                             tag: 'a',
19359                             href: '#',
19360                             cls: 'btn',
19361                             cn: [
19362                                 {
19363                                     tag: 'span',
19364                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19365                                 }
19366                             ]
19367                         } 
19368                     ]
19369                 },
19370                 {
19371                     tag: 'td',
19372                     cls: 'separator'
19373                 },
19374                 {
19375                     tag: 'td',
19376                     cn: [
19377                         {
19378                             tag: 'a',
19379                             href: '#',
19380                             cls: 'btn',
19381                             cn: [
19382                                 {
19383                                     tag: 'span',
19384                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19385                                 }
19386                             ]
19387                         }
19388                     ]
19389                 },
19390                 {
19391                     tag: 'td',
19392                     cls: 'separator'
19393                 }
19394             ]
19395         });
19396         
19397         time.createChild({
19398             tag: 'tr',
19399             cn: [
19400                 {
19401                     tag: 'td',
19402                     cn: [
19403                         {
19404                             tag: 'span',
19405                             cls: 'timepicker-hour',
19406                             html: '00'
19407                         }  
19408                     ]
19409                 },
19410                 {
19411                     tag: 'td',
19412                     cls: 'separator',
19413                     html: ':'
19414                 },
19415                 {
19416                     tag: 'td',
19417                     cn: [
19418                         {
19419                             tag: 'span',
19420                             cls: 'timepicker-minute',
19421                             html: '00'
19422                         }  
19423                     ]
19424                 },
19425                 {
19426                     tag: 'td',
19427                     cls: 'separator'
19428                 },
19429                 {
19430                     tag: 'td',
19431                     cn: [
19432                         {
19433                             tag: 'button',
19434                             type: 'button',
19435                             cls: 'btn btn-primary period',
19436                             html: 'AM'
19437                             
19438                         }
19439                     ]
19440                 }
19441             ]
19442         });
19443         
19444         time.createChild({
19445             tag: 'tr',
19446             cn: [
19447                 {
19448                     tag: 'td',
19449                     cn: [
19450                         {
19451                             tag: 'a',
19452                             href: '#',
19453                             cls: 'btn',
19454                             cn: [
19455                                 {
19456                                     tag: 'span',
19457                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19458                                 }
19459                             ]
19460                         }
19461                     ]
19462                 },
19463                 {
19464                     tag: 'td',
19465                     cls: 'separator'
19466                 },
19467                 {
19468                     tag: 'td',
19469                     cn: [
19470                         {
19471                             tag: 'a',
19472                             href: '#',
19473                             cls: 'btn',
19474                             cn: [
19475                                 {
19476                                     tag: 'span',
19477                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19478                                 }
19479                             ]
19480                         }
19481                     ]
19482                 },
19483                 {
19484                     tag: 'td',
19485                     cls: 'separator'
19486                 }
19487             ]
19488         });
19489         
19490     },
19491     
19492     update: function()
19493     {
19494         
19495         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19496         
19497         this.fill();
19498     },
19499     
19500     fill: function() 
19501     {
19502         var hours = this.time.getHours();
19503         var minutes = this.time.getMinutes();
19504         var period = 'AM';
19505         
19506         if(hours > 11){
19507             period = 'PM';
19508         }
19509         
19510         if(hours == 0){
19511             hours = 12;
19512         }
19513         
19514         
19515         if(hours > 12){
19516             hours = hours - 12;
19517         }
19518         
19519         if(hours < 10){
19520             hours = '0' + hours;
19521         }
19522         
19523         if(minutes < 10){
19524             minutes = '0' + minutes;
19525         }
19526         
19527         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19528         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19529         this.pop.select('button', true).first().dom.innerHTML = period;
19530         
19531     },
19532     
19533     place: function()
19534     {   
19535         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19536         
19537         var cls = ['bottom'];
19538         
19539         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19540             cls.pop();
19541             cls.push('top');
19542         }
19543         
19544         cls.push('right');
19545         
19546         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19547             cls.pop();
19548             cls.push('left');
19549         }
19550         
19551         this.picker().addClass(cls.join('-'));
19552         
19553         var _this = this;
19554         
19555         Roo.each(cls, function(c){
19556             if(c == 'bottom'){
19557                 _this.picker().setTop(_this.inputEl().getHeight());
19558                 return;
19559             }
19560             if(c == 'top'){
19561                 _this.picker().setTop(0 - _this.picker().getHeight());
19562                 return;
19563             }
19564             
19565             if(c == 'left'){
19566                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19567                 return;
19568             }
19569             if(c == 'right'){
19570                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19571                 return;
19572             }
19573         });
19574         
19575     },
19576   
19577     onFocus : function()
19578     {
19579         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19580         this.show();
19581     },
19582     
19583     onBlur : function()
19584     {
19585         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19586         this.hide();
19587     },
19588     
19589     show : function()
19590     {
19591         this.picker().show();
19592         this.pop.show();
19593         this.update();
19594         this.place();
19595         
19596         this.fireEvent('show', this, this.date);
19597     },
19598     
19599     hide : function()
19600     {
19601         this.picker().hide();
19602         this.pop.hide();
19603         
19604         this.fireEvent('hide', this, this.date);
19605     },
19606     
19607     setTime : function()
19608     {
19609         this.hide();
19610         this.setValue(this.time.format(this.format));
19611         
19612         this.fireEvent('select', this, this.date);
19613         
19614         
19615     },
19616     
19617     onMousedown: function(e){
19618         e.stopPropagation();
19619         e.preventDefault();
19620     },
19621     
19622     onIncrementHours: function()
19623     {
19624         Roo.log('onIncrementHours');
19625         this.time = this.time.add(Date.HOUR, 1);
19626         this.update();
19627         
19628     },
19629     
19630     onDecrementHours: function()
19631     {
19632         Roo.log('onDecrementHours');
19633         this.time = this.time.add(Date.HOUR, -1);
19634         this.update();
19635     },
19636     
19637     onIncrementMinutes: function()
19638     {
19639         Roo.log('onIncrementMinutes');
19640         this.time = this.time.add(Date.MINUTE, 1);
19641         this.update();
19642     },
19643     
19644     onDecrementMinutes: function()
19645     {
19646         Roo.log('onDecrementMinutes');
19647         this.time = this.time.add(Date.MINUTE, -1);
19648         this.update();
19649     },
19650     
19651     onTogglePeriod: function()
19652     {
19653         Roo.log('onTogglePeriod');
19654         this.time = this.time.add(Date.HOUR, 12);
19655         this.update();
19656     }
19657     
19658    
19659 });
19660
19661 Roo.apply(Roo.bootstrap.TimeField,  {
19662     
19663     content : {
19664         tag: 'tbody',
19665         cn: [
19666             {
19667                 tag: 'tr',
19668                 cn: [
19669                 {
19670                     tag: 'td',
19671                     colspan: '7'
19672                 }
19673                 ]
19674             }
19675         ]
19676     },
19677     
19678     footer : {
19679         tag: 'tfoot',
19680         cn: [
19681             {
19682                 tag: 'tr',
19683                 cn: [
19684                 {
19685                     tag: 'th',
19686                     colspan: '7',
19687                     cls: '',
19688                     cn: [
19689                         {
19690                             tag: 'button',
19691                             cls: 'btn btn-info ok',
19692                             html: 'OK'
19693                         }
19694                     ]
19695                 }
19696
19697                 ]
19698             }
19699         ]
19700     }
19701 });
19702
19703 Roo.apply(Roo.bootstrap.TimeField,  {
19704   
19705     template : {
19706         tag: 'div',
19707         cls: 'datepicker dropdown-menu',
19708         cn: [
19709             {
19710                 tag: 'div',
19711                 cls: 'datepicker-time',
19712                 cn: [
19713                 {
19714                     tag: 'table',
19715                     cls: 'table-condensed',
19716                     cn:[
19717                     Roo.bootstrap.TimeField.content,
19718                     Roo.bootstrap.TimeField.footer
19719                     ]
19720                 }
19721                 ]
19722             }
19723         ]
19724     }
19725 });
19726
19727  
19728
19729  /*
19730  * - LGPL
19731  *
19732  * MonthField
19733  * 
19734  */
19735
19736 /**
19737  * @class Roo.bootstrap.MonthField
19738  * @extends Roo.bootstrap.Input
19739  * Bootstrap MonthField class
19740  * 
19741  * @cfg {String} language default en
19742  * 
19743  * @constructor
19744  * Create a new MonthField
19745  * @param {Object} config The config object
19746  */
19747
19748 Roo.bootstrap.MonthField = function(config){
19749     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19750     
19751     this.addEvents({
19752         /**
19753          * @event show
19754          * Fires when this field show.
19755          * @param {Roo.bootstrap.MonthField} this
19756          * @param {Mixed} date The date value
19757          */
19758         show : true,
19759         /**
19760          * @event show
19761          * Fires when this field hide.
19762          * @param {Roo.bootstrap.MonthField} this
19763          * @param {Mixed} date The date value
19764          */
19765         hide : true,
19766         /**
19767          * @event select
19768          * Fires when select a date.
19769          * @param {Roo.bootstrap.MonthField} this
19770          * @param {String} oldvalue The old value
19771          * @param {String} newvalue The new value
19772          */
19773         select : true
19774     });
19775 };
19776
19777 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19778     
19779     onRender: function(ct, position)
19780     {
19781         
19782         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19783         
19784         this.language = this.language || 'en';
19785         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19786         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19787         
19788         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19789         this.isInline = false;
19790         this.isInput = true;
19791         this.component = this.el.select('.add-on', true).first() || false;
19792         this.component = (this.component && this.component.length === 0) ? false : this.component;
19793         this.hasInput = this.component && this.inputEL().length;
19794         
19795         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19796         
19797         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19798         
19799         this.picker().on('mousedown', this.onMousedown, this);
19800         this.picker().on('click', this.onClick, this);
19801         
19802         this.picker().addClass('datepicker-dropdown');
19803         
19804         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19805             v.setStyle('width', '189px');
19806         });
19807         
19808         this.fillMonths();
19809         
19810         this.update();
19811         
19812         if(this.isInline) {
19813             this.show();
19814         }
19815         
19816     },
19817     
19818     setValue: function(v, suppressEvent)
19819     {   
19820         var o = this.getValue();
19821         
19822         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19823         
19824         this.update();
19825
19826         if(suppressEvent !== true){
19827             this.fireEvent('select', this, o, v);
19828         }
19829         
19830     },
19831     
19832     getValue: function()
19833     {
19834         return this.value;
19835     },
19836     
19837     onClick: function(e) 
19838     {
19839         e.stopPropagation();
19840         e.preventDefault();
19841         
19842         var target = e.getTarget();
19843         
19844         if(target.nodeName.toLowerCase() === 'i'){
19845             target = Roo.get(target).dom.parentNode;
19846         }
19847         
19848         var nodeName = target.nodeName;
19849         var className = target.className;
19850         var html = target.innerHTML;
19851         
19852         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19853             return;
19854         }
19855         
19856         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19857         
19858         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19859         
19860         this.hide();
19861                         
19862     },
19863     
19864     picker : function()
19865     {
19866         return this.pickerEl;
19867     },
19868     
19869     fillMonths: function()
19870     {    
19871         var i = 0;
19872         var months = this.picker().select('>.datepicker-months td', true).first();
19873         
19874         months.dom.innerHTML = '';
19875         
19876         while (i < 12) {
19877             var month = {
19878                 tag: 'span',
19879                 cls: 'month',
19880                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19881             };
19882             
19883             months.createChild(month);
19884         }
19885         
19886     },
19887     
19888     update: function()
19889     {
19890         var _this = this;
19891         
19892         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19893             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19894         }
19895         
19896         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19897             e.removeClass('active');
19898             
19899             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19900                 e.addClass('active');
19901             }
19902         })
19903     },
19904     
19905     place: function()
19906     {
19907         if(this.isInline) {
19908             return;
19909         }
19910         
19911         this.picker().removeClass(['bottom', 'top']);
19912         
19913         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19914             /*
19915              * place to the top of element!
19916              *
19917              */
19918             
19919             this.picker().addClass('top');
19920             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19921             
19922             return;
19923         }
19924         
19925         this.picker().addClass('bottom');
19926         
19927         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19928     },
19929     
19930     onFocus : function()
19931     {
19932         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19933         this.show();
19934     },
19935     
19936     onBlur : function()
19937     {
19938         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19939         
19940         var d = this.inputEl().getValue();
19941         
19942         this.setValue(d);
19943                 
19944         this.hide();
19945     },
19946     
19947     show : function()
19948     {
19949         this.picker().show();
19950         this.picker().select('>.datepicker-months', true).first().show();
19951         this.update();
19952         this.place();
19953         
19954         this.fireEvent('show', this, this.date);
19955     },
19956     
19957     hide : function()
19958     {
19959         if(this.isInline) {
19960             return;
19961         }
19962         this.picker().hide();
19963         this.fireEvent('hide', this, this.date);
19964         
19965     },
19966     
19967     onMousedown: function(e)
19968     {
19969         e.stopPropagation();
19970         e.preventDefault();
19971     },
19972     
19973     keyup: function(e)
19974     {
19975         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19976         this.update();
19977     },
19978
19979     fireKey: function(e)
19980     {
19981         if (!this.picker().isVisible()){
19982             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19983                 this.show();
19984             }
19985             return;
19986         }
19987         
19988         var dir;
19989         
19990         switch(e.keyCode){
19991             case 27: // escape
19992                 this.hide();
19993                 e.preventDefault();
19994                 break;
19995             case 37: // left
19996             case 39: // right
19997                 dir = e.keyCode == 37 ? -1 : 1;
19998                 
19999                 this.vIndex = this.vIndex + dir;
20000                 
20001                 if(this.vIndex < 0){
20002                     this.vIndex = 0;
20003                 }
20004                 
20005                 if(this.vIndex > 11){
20006                     this.vIndex = 11;
20007                 }
20008                 
20009                 if(isNaN(this.vIndex)){
20010                     this.vIndex = 0;
20011                 }
20012                 
20013                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20014                 
20015                 break;
20016             case 38: // up
20017             case 40: // down
20018                 
20019                 dir = e.keyCode == 38 ? -1 : 1;
20020                 
20021                 this.vIndex = this.vIndex + dir * 4;
20022                 
20023                 if(this.vIndex < 0){
20024                     this.vIndex = 0;
20025                 }
20026                 
20027                 if(this.vIndex > 11){
20028                     this.vIndex = 11;
20029                 }
20030                 
20031                 if(isNaN(this.vIndex)){
20032                     this.vIndex = 0;
20033                 }
20034                 
20035                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20036                 break;
20037                 
20038             case 13: // enter
20039                 
20040                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20041                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20042                 }
20043                 
20044                 this.hide();
20045                 e.preventDefault();
20046                 break;
20047             case 9: // tab
20048                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20049                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20050                 }
20051                 this.hide();
20052                 break;
20053             case 16: // shift
20054             case 17: // ctrl
20055             case 18: // alt
20056                 break;
20057             default :
20058                 this.hide();
20059                 
20060         }
20061     },
20062     
20063     remove: function() 
20064     {
20065         this.picker().remove();
20066     }
20067    
20068 });
20069
20070 Roo.apply(Roo.bootstrap.MonthField,  {
20071     
20072     content : {
20073         tag: 'tbody',
20074         cn: [
20075         {
20076             tag: 'tr',
20077             cn: [
20078             {
20079                 tag: 'td',
20080                 colspan: '7'
20081             }
20082             ]
20083         }
20084         ]
20085     },
20086     
20087     dates:{
20088         en: {
20089             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20090             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20091         }
20092     }
20093 });
20094
20095 Roo.apply(Roo.bootstrap.MonthField,  {
20096   
20097     template : {
20098         tag: 'div',
20099         cls: 'datepicker dropdown-menu roo-dynamic',
20100         cn: [
20101             {
20102                 tag: 'div',
20103                 cls: 'datepicker-months',
20104                 cn: [
20105                 {
20106                     tag: 'table',
20107                     cls: 'table-condensed',
20108                     cn:[
20109                         Roo.bootstrap.DateField.content
20110                     ]
20111                 }
20112                 ]
20113             }
20114         ]
20115     }
20116 });
20117
20118  
20119
20120  
20121  /*
20122  * - LGPL
20123  *
20124  * CheckBox
20125  * 
20126  */
20127
20128 /**
20129  * @class Roo.bootstrap.CheckBox
20130  * @extends Roo.bootstrap.Input
20131  * Bootstrap CheckBox class
20132  * 
20133  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20134  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20135  * @cfg {String} boxLabel The text that appears beside the checkbox
20136  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20137  * @cfg {Boolean} checked initnal the element
20138  * @cfg {Boolean} inline inline the element (default false)
20139  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20140  * @cfg {String} tooltip label tooltip
20141  * 
20142  * @constructor
20143  * Create a new CheckBox
20144  * @param {Object} config The config object
20145  */
20146
20147 Roo.bootstrap.CheckBox = function(config){
20148     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20149    
20150     this.addEvents({
20151         /**
20152         * @event check
20153         * Fires when the element is checked or unchecked.
20154         * @param {Roo.bootstrap.CheckBox} this This input
20155         * @param {Boolean} checked The new checked value
20156         */
20157        check : true,
20158        /**
20159         * @event click
20160         * Fires when the element is click.
20161         * @param {Roo.bootstrap.CheckBox} this This input
20162         */
20163        click : true
20164     });
20165     
20166 };
20167
20168 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20169   
20170     inputType: 'checkbox',
20171     inputValue: 1,
20172     valueOff: 0,
20173     boxLabel: false,
20174     checked: false,
20175     weight : false,
20176     inline: false,
20177     tooltip : '',
20178     
20179     getAutoCreate : function()
20180     {
20181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20182         
20183         var id = Roo.id();
20184         
20185         var cfg = {};
20186         
20187         cfg.cls = 'form-group ' + this.inputType; //input-group
20188         
20189         if(this.inline){
20190             cfg.cls += ' ' + this.inputType + '-inline';
20191         }
20192         
20193         var input =  {
20194             tag: 'input',
20195             id : id,
20196             type : this.inputType,
20197             value : this.inputValue,
20198             cls : 'roo-' + this.inputType, //'form-box',
20199             placeholder : this.placeholder || ''
20200             
20201         };
20202         
20203         if(this.inputType != 'radio'){
20204             var hidden =  {
20205                 tag: 'input',
20206                 type : 'hidden',
20207                 cls : 'roo-hidden-value',
20208                 value : this.checked ? this.inputValue : this.valueOff
20209             };
20210         }
20211         
20212             
20213         if (this.weight) { // Validity check?
20214             cfg.cls += " " + this.inputType + "-" + this.weight;
20215         }
20216         
20217         if (this.disabled) {
20218             input.disabled=true;
20219         }
20220         
20221         if(this.checked){
20222             input.checked = this.checked;
20223         }
20224         
20225         if (this.name) {
20226             
20227             input.name = this.name;
20228             
20229             if(this.inputType != 'radio'){
20230                 hidden.name = this.name;
20231                 input.name = '_hidden_' + this.name;
20232             }
20233         }
20234         
20235         if (this.size) {
20236             input.cls += ' input-' + this.size;
20237         }
20238         
20239         var settings=this;
20240         
20241         ['xs','sm','md','lg'].map(function(size){
20242             if (settings[size]) {
20243                 cfg.cls += ' col-' + size + '-' + settings[size];
20244             }
20245         });
20246         
20247         var inputblock = input;
20248          
20249         if (this.before || this.after) {
20250             
20251             inputblock = {
20252                 cls : 'input-group',
20253                 cn :  [] 
20254             };
20255             
20256             if (this.before) {
20257                 inputblock.cn.push({
20258                     tag :'span',
20259                     cls : 'input-group-addon',
20260                     html : this.before
20261                 });
20262             }
20263             
20264             inputblock.cn.push(input);
20265             
20266             if(this.inputType != 'radio'){
20267                 inputblock.cn.push(hidden);
20268             }
20269             
20270             if (this.after) {
20271                 inputblock.cn.push({
20272                     tag :'span',
20273                     cls : 'input-group-addon',
20274                     html : this.after
20275                 });
20276             }
20277             
20278         }
20279         
20280         if (align ==='left' && this.fieldLabel.length) {
20281 //                Roo.log("left and has label");
20282             cfg.cn = [
20283                 {
20284                     tag: 'label',
20285                     'for' :  id,
20286                     cls : 'control-label',
20287                     html : this.fieldLabel
20288                 },
20289                 {
20290                     cls : "", 
20291                     cn: [
20292                         inputblock
20293                     ]
20294                 }
20295             ];
20296             
20297             if(this.labelWidth > 12){
20298                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20299             }
20300             
20301             if(this.labelWidth < 13 && this.labelmd == 0){
20302                 this.labelmd = this.labelWidth;
20303             }
20304             
20305             if(this.labellg > 0){
20306                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20307                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20308             }
20309             
20310             if(this.labelmd > 0){
20311                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20312                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20313             }
20314             
20315             if(this.labelsm > 0){
20316                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20317                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20318             }
20319             
20320             if(this.labelxs > 0){
20321                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20322                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20323             }
20324             
20325         } else if ( this.fieldLabel.length) {
20326 //                Roo.log(" label");
20327                 cfg.cn = [
20328                    
20329                     {
20330                         tag: this.boxLabel ? 'span' : 'label',
20331                         'for': id,
20332                         cls: 'control-label box-input-label',
20333                         //cls : 'input-group-addon',
20334                         html : this.fieldLabel
20335                     },
20336                     
20337                     inputblock
20338                     
20339                 ];
20340
20341         } else {
20342             
20343 //                Roo.log(" no label && no align");
20344                 cfg.cn = [  inputblock ] ;
20345                 
20346                 
20347         }
20348         
20349         if(this.boxLabel){
20350              var boxLabelCfg = {
20351                 tag: 'label',
20352                 //'for': id, // box label is handled by onclick - so no for...
20353                 cls: 'box-label',
20354                 html: this.boxLabel
20355             };
20356             
20357             if(this.tooltip){
20358                 boxLabelCfg.tooltip = this.tooltip;
20359             }
20360              
20361             cfg.cn.push(boxLabelCfg);
20362         }
20363         
20364         if(this.inputType != 'radio'){
20365             cfg.cn.push(hidden);
20366         }
20367         
20368         return cfg;
20369         
20370     },
20371     
20372     /**
20373      * return the real input element.
20374      */
20375     inputEl: function ()
20376     {
20377         return this.el.select('input.roo-' + this.inputType,true).first();
20378     },
20379     hiddenEl: function ()
20380     {
20381         return this.el.select('input.roo-hidden-value',true).first();
20382     },
20383     
20384     labelEl: function()
20385     {
20386         return this.el.select('label.control-label',true).first();
20387     },
20388     /* depricated... */
20389     
20390     label: function()
20391     {
20392         return this.labelEl();
20393     },
20394     
20395     boxLabelEl: function()
20396     {
20397         return this.el.select('label.box-label',true).first();
20398     },
20399     
20400     initEvents : function()
20401     {
20402 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20403         
20404         this.inputEl().on('click', this.onClick,  this);
20405         
20406         if (this.boxLabel) { 
20407             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20408         }
20409         
20410         this.startValue = this.getValue();
20411         
20412         if(this.groupId){
20413             Roo.bootstrap.CheckBox.register(this);
20414         }
20415     },
20416     
20417     onClick : function(e)
20418     {   
20419         if(this.fireEvent('click', this, e) !== false){
20420             this.setChecked(!this.checked);
20421         }
20422         
20423     },
20424     
20425     setChecked : function(state,suppressEvent)
20426     {
20427         this.startValue = this.getValue();
20428
20429         if(this.inputType == 'radio'){
20430             
20431             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20432                 e.dom.checked = false;
20433             });
20434             
20435             this.inputEl().dom.checked = true;
20436             
20437             this.inputEl().dom.value = this.inputValue;
20438             
20439             if(suppressEvent !== true){
20440                 this.fireEvent('check', this, true);
20441             }
20442             
20443             this.validate();
20444             
20445             return;
20446         }
20447         
20448         this.checked = state;
20449         
20450         this.inputEl().dom.checked = state;
20451         
20452         
20453         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20454         
20455         if(suppressEvent !== true){
20456             this.fireEvent('check', this, state);
20457         }
20458         
20459         this.validate();
20460     },
20461     
20462     getValue : function()
20463     {
20464         if(this.inputType == 'radio'){
20465             return this.getGroupValue();
20466         }
20467         
20468         return this.hiddenEl().dom.value;
20469         
20470     },
20471     
20472     getGroupValue : function()
20473     {
20474         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20475             return '';
20476         }
20477         
20478         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20479     },
20480     
20481     setValue : function(v,suppressEvent)
20482     {
20483         if(this.inputType == 'radio'){
20484             this.setGroupValue(v, suppressEvent);
20485             return;
20486         }
20487         
20488         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20489         
20490         this.validate();
20491     },
20492     
20493     setGroupValue : function(v, suppressEvent)
20494     {
20495         this.startValue = this.getValue();
20496         
20497         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20498             e.dom.checked = false;
20499             
20500             if(e.dom.value == v){
20501                 e.dom.checked = true;
20502             }
20503         });
20504         
20505         if(suppressEvent !== true){
20506             this.fireEvent('check', this, true);
20507         }
20508
20509         this.validate();
20510         
20511         return;
20512     },
20513     
20514     validate : function()
20515     {
20516         if(
20517                 this.disabled || 
20518                 (this.inputType == 'radio' && this.validateRadio()) ||
20519                 (this.inputType == 'checkbox' && this.validateCheckbox())
20520         ){
20521             this.markValid();
20522             return true;
20523         }
20524         
20525         this.markInvalid();
20526         return false;
20527     },
20528     
20529     validateRadio : function()
20530     {
20531         if(this.allowBlank){
20532             return true;
20533         }
20534         
20535         var valid = false;
20536         
20537         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20538             if(!e.dom.checked){
20539                 return;
20540             }
20541             
20542             valid = true;
20543             
20544             return false;
20545         });
20546         
20547         return valid;
20548     },
20549     
20550     validateCheckbox : function()
20551     {
20552         if(!this.groupId){
20553             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20554             //return (this.getValue() == this.inputValue) ? true : false;
20555         }
20556         
20557         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20558         
20559         if(!group){
20560             return false;
20561         }
20562         
20563         var r = false;
20564         
20565         for(var i in group){
20566             if(group[i].el.isVisible(true)){
20567                 r = false;
20568                 break;
20569             }
20570             
20571             r = true;
20572         }
20573         
20574         for(var i in group){
20575             if(r){
20576                 break;
20577             }
20578             
20579             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20580         }
20581         
20582         return r;
20583     },
20584     
20585     /**
20586      * Mark this field as valid
20587      */
20588     markValid : function()
20589     {
20590         var _this = this;
20591         
20592         this.fireEvent('valid', this);
20593         
20594         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20595         
20596         if(this.groupId){
20597             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20598         }
20599         
20600         if(label){
20601             label.markValid();
20602         }
20603
20604         if(this.inputType == 'radio'){
20605             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20606                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20607                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20608             });
20609             
20610             return;
20611         }
20612
20613         if(!this.groupId){
20614             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20615             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20616             return;
20617         }
20618         
20619         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20620         
20621         if(!group){
20622             return;
20623         }
20624         
20625         for(var i in group){
20626             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20627             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20628         }
20629     },
20630     
20631      /**
20632      * Mark this field as invalid
20633      * @param {String} msg The validation message
20634      */
20635     markInvalid : function(msg)
20636     {
20637         if(this.allowBlank){
20638             return;
20639         }
20640         
20641         var _this = this;
20642         
20643         this.fireEvent('invalid', this, msg);
20644         
20645         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20646         
20647         if(this.groupId){
20648             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20649         }
20650         
20651         if(label){
20652             label.markInvalid();
20653         }
20654             
20655         if(this.inputType == 'radio'){
20656             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20657                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20658                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20659             });
20660             
20661             return;
20662         }
20663         
20664         if(!this.groupId){
20665             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20666             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20667             return;
20668         }
20669         
20670         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20671         
20672         if(!group){
20673             return;
20674         }
20675         
20676         for(var i in group){
20677             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20678             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20679         }
20680         
20681     },
20682     
20683     clearInvalid : function()
20684     {
20685         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20686         
20687         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20688         
20689         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20690         
20691         if (label && label.iconEl) {
20692             label.iconEl.removeClass(label.validClass);
20693             label.iconEl.removeClass(label.invalidClass);
20694         }
20695     },
20696     
20697     disable : function()
20698     {
20699         if(this.inputType != 'radio'){
20700             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20701             return;
20702         }
20703         
20704         var _this = this;
20705         
20706         if(this.rendered){
20707             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20708                 _this.getActionEl().addClass(this.disabledClass);
20709                 e.dom.disabled = true;
20710             });
20711         }
20712         
20713         this.disabled = true;
20714         this.fireEvent("disable", this);
20715         return this;
20716     },
20717
20718     enable : function()
20719     {
20720         if(this.inputType != 'radio'){
20721             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20722             return;
20723         }
20724         
20725         var _this = this;
20726         
20727         if(this.rendered){
20728             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20729                 _this.getActionEl().removeClass(this.disabledClass);
20730                 e.dom.disabled = false;
20731             });
20732         }
20733         
20734         this.disabled = false;
20735         this.fireEvent("enable", this);
20736         return this;
20737     }
20738
20739 });
20740
20741 Roo.apply(Roo.bootstrap.CheckBox, {
20742     
20743     groups: {},
20744     
20745      /**
20746     * register a CheckBox Group
20747     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20748     */
20749     register : function(checkbox)
20750     {
20751         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20752             this.groups[checkbox.groupId] = {};
20753         }
20754         
20755         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20756             return;
20757         }
20758         
20759         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20760         
20761     },
20762     /**
20763     * fetch a CheckBox Group based on the group ID
20764     * @param {string} the group ID
20765     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20766     */
20767     get: function(groupId) {
20768         if (typeof(this.groups[groupId]) == 'undefined') {
20769             return false;
20770         }
20771         
20772         return this.groups[groupId] ;
20773     }
20774     
20775     
20776 });
20777 /*
20778  * - LGPL
20779  *
20780  * RadioItem
20781  * 
20782  */
20783
20784 /**
20785  * @class Roo.bootstrap.Radio
20786  * @extends Roo.bootstrap.Component
20787  * Bootstrap Radio class
20788  * @cfg {String} boxLabel - the label associated
20789  * @cfg {String} value - the value of radio
20790  * 
20791  * @constructor
20792  * Create a new Radio
20793  * @param {Object} config The config object
20794  */
20795 Roo.bootstrap.Radio = function(config){
20796     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20797     
20798 };
20799
20800 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20801     
20802     boxLabel : '',
20803     
20804     value : '',
20805     
20806     getAutoCreate : function()
20807     {
20808         var cfg = {
20809             tag : 'div',
20810             cls : 'form-group radio',
20811             cn : [
20812                 {
20813                     tag : 'label',
20814                     cls : 'box-label',
20815                     html : this.boxLabel
20816                 }
20817             ]
20818         };
20819         
20820         return cfg;
20821     },
20822     
20823     initEvents : function() 
20824     {
20825         this.parent().register(this);
20826         
20827         this.el.on('click', this.onClick, this);
20828         
20829     },
20830     
20831     onClick : function()
20832     {
20833         this.setChecked(true);
20834     },
20835     
20836     setChecked : function(state, suppressEvent)
20837     {
20838         this.parent().setValue(this.value, suppressEvent);
20839         
20840     },
20841     
20842     setBoxLabel : function(v)
20843     {
20844         this.boxLabel = v;
20845         
20846         if(this.rendered){
20847             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20848         }
20849     }
20850     
20851 });
20852  
20853
20854  /*
20855  * - LGPL
20856  *
20857  * Input
20858  * 
20859  */
20860
20861 /**
20862  * @class Roo.bootstrap.SecurePass
20863  * @extends Roo.bootstrap.Input
20864  * Bootstrap SecurePass class
20865  *
20866  * 
20867  * @constructor
20868  * Create a new SecurePass
20869  * @param {Object} config The config object
20870  */
20871  
20872 Roo.bootstrap.SecurePass = function (config) {
20873     // these go here, so the translation tool can replace them..
20874     this.errors = {
20875         PwdEmpty: "Please type a password, and then retype it to confirm.",
20876         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20877         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20878         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20879         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20880         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20881         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20882         TooWeak: "Your password is Too Weak."
20883     },
20884     this.meterLabel = "Password strength:";
20885     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20886     this.meterClass = [
20887         "roo-password-meter-tooweak", 
20888         "roo-password-meter-weak", 
20889         "roo-password-meter-medium", 
20890         "roo-password-meter-strong", 
20891         "roo-password-meter-grey"
20892     ];
20893     
20894     this.errors = {};
20895     
20896     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20897 }
20898
20899 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20900     /**
20901      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20902      * {
20903      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20904      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20905      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20906      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20907      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20908      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20909      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20910      * })
20911      */
20912     // private
20913     
20914     meterWidth: 300,
20915     errorMsg :'',    
20916     errors: false,
20917     imageRoot: '/',
20918     /**
20919      * @cfg {String/Object} Label for the strength meter (defaults to
20920      * 'Password strength:')
20921      */
20922     // private
20923     meterLabel: '',
20924     /**
20925      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20926      * ['Weak', 'Medium', 'Strong'])
20927      */
20928     // private    
20929     pwdStrengths: false,    
20930     // private
20931     strength: 0,
20932     // private
20933     _lastPwd: null,
20934     // private
20935     kCapitalLetter: 0,
20936     kSmallLetter: 1,
20937     kDigit: 2,
20938     kPunctuation: 3,
20939     
20940     insecure: false,
20941     // private
20942     initEvents: function ()
20943     {
20944         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20945
20946         if (this.el.is('input[type=password]') && Roo.isSafari) {
20947             this.el.on('keydown', this.SafariOnKeyDown, this);
20948         }
20949
20950         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20951     },
20952     // private
20953     onRender: function (ct, position)
20954     {
20955         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20956         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20957         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20958
20959         this.trigger.createChild({
20960                    cn: [
20961                     {
20962                     //id: 'PwdMeter',
20963                     tag: 'div',
20964                     cls: 'roo-password-meter-grey col-xs-12',
20965                     style: {
20966                         //width: 0,
20967                         //width: this.meterWidth + 'px'                                                
20968                         }
20969                     },
20970                     {                            
20971                          cls: 'roo-password-meter-text'                          
20972                     }
20973                 ]            
20974         });
20975
20976          
20977         if (this.hideTrigger) {
20978             this.trigger.setDisplayed(false);
20979         }
20980         this.setSize(this.width || '', this.height || '');
20981     },
20982     // private
20983     onDestroy: function ()
20984     {
20985         if (this.trigger) {
20986             this.trigger.removeAllListeners();
20987             this.trigger.remove();
20988         }
20989         if (this.wrap) {
20990             this.wrap.remove();
20991         }
20992         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20993     },
20994     // private
20995     checkStrength: function ()
20996     {
20997         var pwd = this.inputEl().getValue();
20998         if (pwd == this._lastPwd) {
20999             return;
21000         }
21001
21002         var strength;
21003         if (this.ClientSideStrongPassword(pwd)) {
21004             strength = 3;
21005         } else if (this.ClientSideMediumPassword(pwd)) {
21006             strength = 2;
21007         } else if (this.ClientSideWeakPassword(pwd)) {
21008             strength = 1;
21009         } else {
21010             strength = 0;
21011         }
21012         
21013         Roo.log('strength1: ' + strength);
21014         
21015         //var pm = this.trigger.child('div/div/div').dom;
21016         var pm = this.trigger.child('div/div');
21017         pm.removeClass(this.meterClass);
21018         pm.addClass(this.meterClass[strength]);
21019                 
21020         
21021         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21022                 
21023         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21024         
21025         this._lastPwd = pwd;
21026     },
21027     reset: function ()
21028     {
21029         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21030         
21031         this._lastPwd = '';
21032         
21033         var pm = this.trigger.child('div/div');
21034         pm.removeClass(this.meterClass);
21035         pm.addClass('roo-password-meter-grey');        
21036         
21037         
21038         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21039         
21040         pt.innerHTML = '';
21041         this.inputEl().dom.type='password';
21042     },
21043     // private
21044     validateValue: function (value)
21045     {
21046         
21047         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21048             return false;
21049         }
21050         if (value.length == 0) {
21051             if (this.allowBlank) {
21052                 this.clearInvalid();
21053                 return true;
21054             }
21055
21056             this.markInvalid(this.errors.PwdEmpty);
21057             this.errorMsg = this.errors.PwdEmpty;
21058             return false;
21059         }
21060         
21061         if(this.insecure){
21062             return true;
21063         }
21064         
21065         if ('[\x21-\x7e]*'.match(value)) {
21066             this.markInvalid(this.errors.PwdBadChar);
21067             this.errorMsg = this.errors.PwdBadChar;
21068             return false;
21069         }
21070         if (value.length < 6) {
21071             this.markInvalid(this.errors.PwdShort);
21072             this.errorMsg = this.errors.PwdShort;
21073             return false;
21074         }
21075         if (value.length > 16) {
21076             this.markInvalid(this.errors.PwdLong);
21077             this.errorMsg = this.errors.PwdLong;
21078             return false;
21079         }
21080         var strength;
21081         if (this.ClientSideStrongPassword(value)) {
21082             strength = 3;
21083         } else if (this.ClientSideMediumPassword(value)) {
21084             strength = 2;
21085         } else if (this.ClientSideWeakPassword(value)) {
21086             strength = 1;
21087         } else {
21088             strength = 0;
21089         }
21090
21091         
21092         if (strength < 2) {
21093             //this.markInvalid(this.errors.TooWeak);
21094             this.errorMsg = this.errors.TooWeak;
21095             //return false;
21096         }
21097         
21098         
21099         console.log('strength2: ' + strength);
21100         
21101         //var pm = this.trigger.child('div/div/div').dom;
21102         
21103         var pm = this.trigger.child('div/div');
21104         pm.removeClass(this.meterClass);
21105         pm.addClass(this.meterClass[strength]);
21106                 
21107         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21108                 
21109         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21110         
21111         this.errorMsg = ''; 
21112         return true;
21113     },
21114     // private
21115     CharacterSetChecks: function (type)
21116     {
21117         this.type = type;
21118         this.fResult = false;
21119     },
21120     // private
21121     isctype: function (character, type)
21122     {
21123         switch (type) {  
21124             case this.kCapitalLetter:
21125                 if (character >= 'A' && character <= 'Z') {
21126                     return true;
21127                 }
21128                 break;
21129             
21130             case this.kSmallLetter:
21131                 if (character >= 'a' && character <= 'z') {
21132                     return true;
21133                 }
21134                 break;
21135             
21136             case this.kDigit:
21137                 if (character >= '0' && character <= '9') {
21138                     return true;
21139                 }
21140                 break;
21141             
21142             case this.kPunctuation:
21143                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21144                     return true;
21145                 }
21146                 break;
21147             
21148             default:
21149                 return false;
21150         }
21151
21152     },
21153     // private
21154     IsLongEnough: function (pwd, size)
21155     {
21156         return !(pwd == null || isNaN(size) || pwd.length < size);
21157     },
21158     // private
21159     SpansEnoughCharacterSets: function (word, nb)
21160     {
21161         if (!this.IsLongEnough(word, nb))
21162         {
21163             return false;
21164         }
21165
21166         var characterSetChecks = new Array(
21167             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21168             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21169         );
21170         
21171         for (var index = 0; index < word.length; ++index) {
21172             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21173                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21174                     characterSetChecks[nCharSet].fResult = true;
21175                     break;
21176                 }
21177             }
21178         }
21179
21180         var nCharSets = 0;
21181         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21182             if (characterSetChecks[nCharSet].fResult) {
21183                 ++nCharSets;
21184             }
21185         }
21186
21187         if (nCharSets < nb) {
21188             return false;
21189         }
21190         return true;
21191     },
21192     // private
21193     ClientSideStrongPassword: function (pwd)
21194     {
21195         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21196     },
21197     // private
21198     ClientSideMediumPassword: function (pwd)
21199     {
21200         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21201     },
21202     // private
21203     ClientSideWeakPassword: function (pwd)
21204     {
21205         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21206     }
21207           
21208 })//<script type="text/javascript">
21209
21210 /*
21211  * Based  Ext JS Library 1.1.1
21212  * Copyright(c) 2006-2007, Ext JS, LLC.
21213  * LGPL
21214  *
21215  */
21216  
21217 /**
21218  * @class Roo.HtmlEditorCore
21219  * @extends Roo.Component
21220  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21221  *
21222  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21223  */
21224
21225 Roo.HtmlEditorCore = function(config){
21226     
21227     
21228     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21229     
21230     
21231     this.addEvents({
21232         /**
21233          * @event initialize
21234          * Fires when the editor is fully initialized (including the iframe)
21235          * @param {Roo.HtmlEditorCore} this
21236          */
21237         initialize: true,
21238         /**
21239          * @event activate
21240          * Fires when the editor is first receives the focus. Any insertion must wait
21241          * until after this event.
21242          * @param {Roo.HtmlEditorCore} this
21243          */
21244         activate: true,
21245          /**
21246          * @event beforesync
21247          * Fires before the textarea is updated with content from the editor iframe. Return false
21248          * to cancel the sync.
21249          * @param {Roo.HtmlEditorCore} this
21250          * @param {String} html
21251          */
21252         beforesync: true,
21253          /**
21254          * @event beforepush
21255          * Fires before the iframe editor is updated with content from the textarea. Return false
21256          * to cancel the push.
21257          * @param {Roo.HtmlEditorCore} this
21258          * @param {String} html
21259          */
21260         beforepush: true,
21261          /**
21262          * @event sync
21263          * Fires when the textarea is updated with content from the editor iframe.
21264          * @param {Roo.HtmlEditorCore} this
21265          * @param {String} html
21266          */
21267         sync: true,
21268          /**
21269          * @event push
21270          * Fires when the iframe editor is updated with content from the textarea.
21271          * @param {Roo.HtmlEditorCore} this
21272          * @param {String} html
21273          */
21274         push: true,
21275         
21276         /**
21277          * @event editorevent
21278          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21279          * @param {Roo.HtmlEditorCore} this
21280          */
21281         editorevent: true
21282         
21283     });
21284     
21285     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21286     
21287     // defaults : white / black...
21288     this.applyBlacklists();
21289     
21290     
21291     
21292 };
21293
21294
21295 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21296
21297
21298      /**
21299      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21300      */
21301     
21302     owner : false,
21303     
21304      /**
21305      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21306      *                        Roo.resizable.
21307      */
21308     resizable : false,
21309      /**
21310      * @cfg {Number} height (in pixels)
21311      */   
21312     height: 300,
21313    /**
21314      * @cfg {Number} width (in pixels)
21315      */   
21316     width: 500,
21317     
21318     /**
21319      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21320      * 
21321      */
21322     stylesheets: false,
21323     
21324     // id of frame..
21325     frameId: false,
21326     
21327     // private properties
21328     validationEvent : false,
21329     deferHeight: true,
21330     initialized : false,
21331     activated : false,
21332     sourceEditMode : false,
21333     onFocus : Roo.emptyFn,
21334     iframePad:3,
21335     hideMode:'offsets',
21336     
21337     clearUp: true,
21338     
21339     // blacklist + whitelisted elements..
21340     black: false,
21341     white: false,
21342      
21343     bodyCls : '',
21344
21345     /**
21346      * Protected method that will not generally be called directly. It
21347      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21348      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21349      */
21350     getDocMarkup : function(){
21351         // body styles..
21352         var st = '';
21353         
21354         // inherit styels from page...?? 
21355         if (this.stylesheets === false) {
21356             
21357             Roo.get(document.head).select('style').each(function(node) {
21358                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21359             });
21360             
21361             Roo.get(document.head).select('link').each(function(node) { 
21362                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21363             });
21364             
21365         } else if (!this.stylesheets.length) {
21366                 // simple..
21367                 st = '<style type="text/css">' +
21368                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21369                    '</style>';
21370         } else { 
21371             st = '<style type="text/css">' +
21372                     this.stylesheets +
21373                 '</style>';
21374         }
21375         
21376         st +=  '<style type="text/css">' +
21377             'IMG { cursor: pointer } ' +
21378         '</style>';
21379
21380         var cls = 'roo-htmleditor-body';
21381         
21382         if(this.bodyCls.length){
21383             cls += ' ' + this.bodyCls;
21384         }
21385         
21386         return '<html><head>' + st  +
21387             //<style type="text/css">' +
21388             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21389             //'</style>' +
21390             ' </head><body class="' +  cls + '"></body></html>';
21391     },
21392
21393     // private
21394     onRender : function(ct, position)
21395     {
21396         var _t = this;
21397         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21398         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21399         
21400         
21401         this.el.dom.style.border = '0 none';
21402         this.el.dom.setAttribute('tabIndex', -1);
21403         this.el.addClass('x-hidden hide');
21404         
21405         
21406         
21407         if(Roo.isIE){ // fix IE 1px bogus margin
21408             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21409         }
21410        
21411         
21412         this.frameId = Roo.id();
21413         
21414          
21415         
21416         var iframe = this.owner.wrap.createChild({
21417             tag: 'iframe',
21418             cls: 'form-control', // bootstrap..
21419             id: this.frameId,
21420             name: this.frameId,
21421             frameBorder : 'no',
21422             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21423         }, this.el
21424         );
21425         
21426         
21427         this.iframe = iframe.dom;
21428
21429          this.assignDocWin();
21430         
21431         this.doc.designMode = 'on';
21432        
21433         this.doc.open();
21434         this.doc.write(this.getDocMarkup());
21435         this.doc.close();
21436
21437         
21438         var task = { // must defer to wait for browser to be ready
21439             run : function(){
21440                 //console.log("run task?" + this.doc.readyState);
21441                 this.assignDocWin();
21442                 if(this.doc.body || this.doc.readyState == 'complete'){
21443                     try {
21444                         this.doc.designMode="on";
21445                     } catch (e) {
21446                         return;
21447                     }
21448                     Roo.TaskMgr.stop(task);
21449                     this.initEditor.defer(10, this);
21450                 }
21451             },
21452             interval : 10,
21453             duration: 10000,
21454             scope: this
21455         };
21456         Roo.TaskMgr.start(task);
21457
21458     },
21459
21460     // private
21461     onResize : function(w, h)
21462     {
21463          Roo.log('resize: ' +w + ',' + h );
21464         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21465         if(!this.iframe){
21466             return;
21467         }
21468         if(typeof w == 'number'){
21469             
21470             this.iframe.style.width = w + 'px';
21471         }
21472         if(typeof h == 'number'){
21473             
21474             this.iframe.style.height = h + 'px';
21475             if(this.doc){
21476                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21477             }
21478         }
21479         
21480     },
21481
21482     /**
21483      * Toggles the editor between standard and source edit mode.
21484      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21485      */
21486     toggleSourceEdit : function(sourceEditMode){
21487         
21488         this.sourceEditMode = sourceEditMode === true;
21489         
21490         if(this.sourceEditMode){
21491  
21492             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21493             
21494         }else{
21495             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21496             //this.iframe.className = '';
21497             this.deferFocus();
21498         }
21499         //this.setSize(this.owner.wrap.getSize());
21500         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21501     },
21502
21503     
21504   
21505
21506     /**
21507      * Protected method that will not generally be called directly. If you need/want
21508      * custom HTML cleanup, this is the method you should override.
21509      * @param {String} html The HTML to be cleaned
21510      * return {String} The cleaned HTML
21511      */
21512     cleanHtml : function(html){
21513         html = String(html);
21514         if(html.length > 5){
21515             if(Roo.isSafari){ // strip safari nonsense
21516                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21517             }
21518         }
21519         if(html == '&nbsp;'){
21520             html = '';
21521         }
21522         return html;
21523     },
21524
21525     /**
21526      * HTML Editor -> Textarea
21527      * Protected method that will not generally be called directly. Syncs the contents
21528      * of the editor iframe with the textarea.
21529      */
21530     syncValue : function(){
21531         if(this.initialized){
21532             var bd = (this.doc.body || this.doc.documentElement);
21533             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21534             var html = bd.innerHTML;
21535             if(Roo.isSafari){
21536                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21537                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21538                 if(m && m[1]){
21539                     html = '<div style="'+m[0]+'">' + html + '</div>';
21540                 }
21541             }
21542             html = this.cleanHtml(html);
21543             // fix up the special chars.. normaly like back quotes in word...
21544             // however we do not want to do this with chinese..
21545             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21546                 var cc = b.charCodeAt();
21547                 if (
21548                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21549                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21550                     (cc >= 0xf900 && cc < 0xfb00 )
21551                 ) {
21552                         return b;
21553                 }
21554                 return "&#"+cc+";" 
21555             });
21556             if(this.owner.fireEvent('beforesync', this, html) !== false){
21557                 this.el.dom.value = html;
21558                 this.owner.fireEvent('sync', this, html);
21559             }
21560         }
21561     },
21562
21563     /**
21564      * Protected method that will not generally be called directly. Pushes the value of the textarea
21565      * into the iframe editor.
21566      */
21567     pushValue : function(){
21568         if(this.initialized){
21569             var v = this.el.dom.value.trim();
21570             
21571 //            if(v.length < 1){
21572 //                v = '&#160;';
21573 //            }
21574             
21575             if(this.owner.fireEvent('beforepush', this, v) !== false){
21576                 var d = (this.doc.body || this.doc.documentElement);
21577                 d.innerHTML = v;
21578                 this.cleanUpPaste();
21579                 this.el.dom.value = d.innerHTML;
21580                 this.owner.fireEvent('push', this, v);
21581             }
21582         }
21583     },
21584
21585     // private
21586     deferFocus : function(){
21587         this.focus.defer(10, this);
21588     },
21589
21590     // doc'ed in Field
21591     focus : function(){
21592         if(this.win && !this.sourceEditMode){
21593             this.win.focus();
21594         }else{
21595             this.el.focus();
21596         }
21597     },
21598     
21599     assignDocWin: function()
21600     {
21601         var iframe = this.iframe;
21602         
21603          if(Roo.isIE){
21604             this.doc = iframe.contentWindow.document;
21605             this.win = iframe.contentWindow;
21606         } else {
21607 //            if (!Roo.get(this.frameId)) {
21608 //                return;
21609 //            }
21610 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21611 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21612             
21613             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21614                 return;
21615             }
21616             
21617             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21618             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21619         }
21620     },
21621     
21622     // private
21623     initEditor : function(){
21624         //console.log("INIT EDITOR");
21625         this.assignDocWin();
21626         
21627         
21628         
21629         this.doc.designMode="on";
21630         this.doc.open();
21631         this.doc.write(this.getDocMarkup());
21632         this.doc.close();
21633         
21634         var dbody = (this.doc.body || this.doc.documentElement);
21635         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21636         // this copies styles from the containing element into thsi one..
21637         // not sure why we need all of this..
21638         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21639         
21640         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21641         //ss['background-attachment'] = 'fixed'; // w3c
21642         dbody.bgProperties = 'fixed'; // ie
21643         //Roo.DomHelper.applyStyles(dbody, ss);
21644         Roo.EventManager.on(this.doc, {
21645             //'mousedown': this.onEditorEvent,
21646             'mouseup': this.onEditorEvent,
21647             'dblclick': this.onEditorEvent,
21648             'click': this.onEditorEvent,
21649             'keyup': this.onEditorEvent,
21650             buffer:100,
21651             scope: this
21652         });
21653         if(Roo.isGecko){
21654             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21655         }
21656         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21657             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21658         }
21659         this.initialized = true;
21660
21661         this.owner.fireEvent('initialize', this);
21662         this.pushValue();
21663     },
21664
21665     // private
21666     onDestroy : function(){
21667         
21668         
21669         
21670         if(this.rendered){
21671             
21672             //for (var i =0; i < this.toolbars.length;i++) {
21673             //    // fixme - ask toolbars for heights?
21674             //    this.toolbars[i].onDestroy();
21675            // }
21676             
21677             //this.wrap.dom.innerHTML = '';
21678             //this.wrap.remove();
21679         }
21680     },
21681
21682     // private
21683     onFirstFocus : function(){
21684         
21685         this.assignDocWin();
21686         
21687         
21688         this.activated = true;
21689          
21690     
21691         if(Roo.isGecko){ // prevent silly gecko errors
21692             this.win.focus();
21693             var s = this.win.getSelection();
21694             if(!s.focusNode || s.focusNode.nodeType != 3){
21695                 var r = s.getRangeAt(0);
21696                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21697                 r.collapse(true);
21698                 this.deferFocus();
21699             }
21700             try{
21701                 this.execCmd('useCSS', true);
21702                 this.execCmd('styleWithCSS', false);
21703             }catch(e){}
21704         }
21705         this.owner.fireEvent('activate', this);
21706     },
21707
21708     // private
21709     adjustFont: function(btn){
21710         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21711         //if(Roo.isSafari){ // safari
21712         //    adjust *= 2;
21713        // }
21714         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21715         if(Roo.isSafari){ // safari
21716             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21717             v =  (v < 10) ? 10 : v;
21718             v =  (v > 48) ? 48 : v;
21719             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21720             
21721         }
21722         
21723         
21724         v = Math.max(1, v+adjust);
21725         
21726         this.execCmd('FontSize', v  );
21727     },
21728
21729     onEditorEvent : function(e)
21730     {
21731         this.owner.fireEvent('editorevent', this, e);
21732       //  this.updateToolbar();
21733         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21734     },
21735
21736     insertTag : function(tg)
21737     {
21738         // could be a bit smarter... -> wrap the current selected tRoo..
21739         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21740             
21741             range = this.createRange(this.getSelection());
21742             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21743             wrappingNode.appendChild(range.extractContents());
21744             range.insertNode(wrappingNode);
21745
21746             return;
21747             
21748             
21749             
21750         }
21751         this.execCmd("formatblock",   tg);
21752         
21753     },
21754     
21755     insertText : function(txt)
21756     {
21757         
21758         
21759         var range = this.createRange();
21760         range.deleteContents();
21761                //alert(Sender.getAttribute('label'));
21762                
21763         range.insertNode(this.doc.createTextNode(txt));
21764     } ,
21765     
21766      
21767
21768     /**
21769      * Executes a Midas editor command on the editor document and performs necessary focus and
21770      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21771      * @param {String} cmd The Midas command
21772      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21773      */
21774     relayCmd : function(cmd, value){
21775         this.win.focus();
21776         this.execCmd(cmd, value);
21777         this.owner.fireEvent('editorevent', this);
21778         //this.updateToolbar();
21779         this.owner.deferFocus();
21780     },
21781
21782     /**
21783      * Executes a Midas editor command directly on the editor document.
21784      * For visual commands, you should use {@link #relayCmd} instead.
21785      * <b>This should only be called after the editor is initialized.</b>
21786      * @param {String} cmd The Midas command
21787      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21788      */
21789     execCmd : function(cmd, value){
21790         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21791         this.syncValue();
21792     },
21793  
21794  
21795    
21796     /**
21797      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21798      * to insert tRoo.
21799      * @param {String} text | dom node.. 
21800      */
21801     insertAtCursor : function(text)
21802     {
21803         
21804         if(!this.activated){
21805             return;
21806         }
21807         /*
21808         if(Roo.isIE){
21809             this.win.focus();
21810             var r = this.doc.selection.createRange();
21811             if(r){
21812                 r.collapse(true);
21813                 r.pasteHTML(text);
21814                 this.syncValue();
21815                 this.deferFocus();
21816             
21817             }
21818             return;
21819         }
21820         */
21821         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21822             this.win.focus();
21823             
21824             
21825             // from jquery ui (MIT licenced)
21826             var range, node;
21827             var win = this.win;
21828             
21829             if (win.getSelection && win.getSelection().getRangeAt) {
21830                 range = win.getSelection().getRangeAt(0);
21831                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21832                 range.insertNode(node);
21833             } else if (win.document.selection && win.document.selection.createRange) {
21834                 // no firefox support
21835                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21836                 win.document.selection.createRange().pasteHTML(txt);
21837             } else {
21838                 // no firefox support
21839                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21840                 this.execCmd('InsertHTML', txt);
21841             } 
21842             
21843             this.syncValue();
21844             
21845             this.deferFocus();
21846         }
21847     },
21848  // private
21849     mozKeyPress : function(e){
21850         if(e.ctrlKey){
21851             var c = e.getCharCode(), cmd;
21852           
21853             if(c > 0){
21854                 c = String.fromCharCode(c).toLowerCase();
21855                 switch(c){
21856                     case 'b':
21857                         cmd = 'bold';
21858                         break;
21859                     case 'i':
21860                         cmd = 'italic';
21861                         break;
21862                     
21863                     case 'u':
21864                         cmd = 'underline';
21865                         break;
21866                     
21867                     case 'v':
21868                         this.cleanUpPaste.defer(100, this);
21869                         return;
21870                         
21871                 }
21872                 if(cmd){
21873                     this.win.focus();
21874                     this.execCmd(cmd);
21875                     this.deferFocus();
21876                     e.preventDefault();
21877                 }
21878                 
21879             }
21880         }
21881     },
21882
21883     // private
21884     fixKeys : function(){ // load time branching for fastest keydown performance
21885         if(Roo.isIE){
21886             return function(e){
21887                 var k = e.getKey(), r;
21888                 if(k == e.TAB){
21889                     e.stopEvent();
21890                     r = this.doc.selection.createRange();
21891                     if(r){
21892                         r.collapse(true);
21893                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21894                         this.deferFocus();
21895                     }
21896                     return;
21897                 }
21898                 
21899                 if(k == e.ENTER){
21900                     r = this.doc.selection.createRange();
21901                     if(r){
21902                         var target = r.parentElement();
21903                         if(!target || target.tagName.toLowerCase() != 'li'){
21904                             e.stopEvent();
21905                             r.pasteHTML('<br />');
21906                             r.collapse(false);
21907                             r.select();
21908                         }
21909                     }
21910                 }
21911                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21912                     this.cleanUpPaste.defer(100, this);
21913                     return;
21914                 }
21915                 
21916                 
21917             };
21918         }else if(Roo.isOpera){
21919             return function(e){
21920                 var k = e.getKey();
21921                 if(k == e.TAB){
21922                     e.stopEvent();
21923                     this.win.focus();
21924                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21925                     this.deferFocus();
21926                 }
21927                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21928                     this.cleanUpPaste.defer(100, this);
21929                     return;
21930                 }
21931                 
21932             };
21933         }else if(Roo.isSafari){
21934             return function(e){
21935                 var k = e.getKey();
21936                 
21937                 if(k == e.TAB){
21938                     e.stopEvent();
21939                     this.execCmd('InsertText','\t');
21940                     this.deferFocus();
21941                     return;
21942                 }
21943                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21944                     this.cleanUpPaste.defer(100, this);
21945                     return;
21946                 }
21947                 
21948              };
21949         }
21950     }(),
21951     
21952     getAllAncestors: function()
21953     {
21954         var p = this.getSelectedNode();
21955         var a = [];
21956         if (!p) {
21957             a.push(p); // push blank onto stack..
21958             p = this.getParentElement();
21959         }
21960         
21961         
21962         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21963             a.push(p);
21964             p = p.parentNode;
21965         }
21966         a.push(this.doc.body);
21967         return a;
21968     },
21969     lastSel : false,
21970     lastSelNode : false,
21971     
21972     
21973     getSelection : function() 
21974     {
21975         this.assignDocWin();
21976         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21977     },
21978     
21979     getSelectedNode: function() 
21980     {
21981         // this may only work on Gecko!!!
21982         
21983         // should we cache this!!!!
21984         
21985         
21986         
21987          
21988         var range = this.createRange(this.getSelection()).cloneRange();
21989         
21990         if (Roo.isIE) {
21991             var parent = range.parentElement();
21992             while (true) {
21993                 var testRange = range.duplicate();
21994                 testRange.moveToElementText(parent);
21995                 if (testRange.inRange(range)) {
21996                     break;
21997                 }
21998                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21999                     break;
22000                 }
22001                 parent = parent.parentElement;
22002             }
22003             return parent;
22004         }
22005         
22006         // is ancestor a text element.
22007         var ac =  range.commonAncestorContainer;
22008         if (ac.nodeType == 3) {
22009             ac = ac.parentNode;
22010         }
22011         
22012         var ar = ac.childNodes;
22013          
22014         var nodes = [];
22015         var other_nodes = [];
22016         var has_other_nodes = false;
22017         for (var i=0;i<ar.length;i++) {
22018             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22019                 continue;
22020             }
22021             // fullly contained node.
22022             
22023             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22024                 nodes.push(ar[i]);
22025                 continue;
22026             }
22027             
22028             // probably selected..
22029             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22030                 other_nodes.push(ar[i]);
22031                 continue;
22032             }
22033             // outer..
22034             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22035                 continue;
22036             }
22037             
22038             
22039             has_other_nodes = true;
22040         }
22041         if (!nodes.length && other_nodes.length) {
22042             nodes= other_nodes;
22043         }
22044         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22045             return false;
22046         }
22047         
22048         return nodes[0];
22049     },
22050     createRange: function(sel)
22051     {
22052         // this has strange effects when using with 
22053         // top toolbar - not sure if it's a great idea.
22054         //this.editor.contentWindow.focus();
22055         if (typeof sel != "undefined") {
22056             try {
22057                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22058             } catch(e) {
22059                 return this.doc.createRange();
22060             }
22061         } else {
22062             return this.doc.createRange();
22063         }
22064     },
22065     getParentElement: function()
22066     {
22067         
22068         this.assignDocWin();
22069         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22070         
22071         var range = this.createRange(sel);
22072          
22073         try {
22074             var p = range.commonAncestorContainer;
22075             while (p.nodeType == 3) { // text node
22076                 p = p.parentNode;
22077             }
22078             return p;
22079         } catch (e) {
22080             return null;
22081         }
22082     
22083     },
22084     /***
22085      *
22086      * Range intersection.. the hard stuff...
22087      *  '-1' = before
22088      *  '0' = hits..
22089      *  '1' = after.
22090      *         [ -- selected range --- ]
22091      *   [fail]                        [fail]
22092      *
22093      *    basically..
22094      *      if end is before start or  hits it. fail.
22095      *      if start is after end or hits it fail.
22096      *
22097      *   if either hits (but other is outside. - then it's not 
22098      *   
22099      *    
22100      **/
22101     
22102     
22103     // @see http://www.thismuchiknow.co.uk/?p=64.
22104     rangeIntersectsNode : function(range, node)
22105     {
22106         var nodeRange = node.ownerDocument.createRange();
22107         try {
22108             nodeRange.selectNode(node);
22109         } catch (e) {
22110             nodeRange.selectNodeContents(node);
22111         }
22112     
22113         var rangeStartRange = range.cloneRange();
22114         rangeStartRange.collapse(true);
22115     
22116         var rangeEndRange = range.cloneRange();
22117         rangeEndRange.collapse(false);
22118     
22119         var nodeStartRange = nodeRange.cloneRange();
22120         nodeStartRange.collapse(true);
22121     
22122         var nodeEndRange = nodeRange.cloneRange();
22123         nodeEndRange.collapse(false);
22124     
22125         return rangeStartRange.compareBoundaryPoints(
22126                  Range.START_TO_START, nodeEndRange) == -1 &&
22127                rangeEndRange.compareBoundaryPoints(
22128                  Range.START_TO_START, nodeStartRange) == 1;
22129         
22130          
22131     },
22132     rangeCompareNode : function(range, node)
22133     {
22134         var nodeRange = node.ownerDocument.createRange();
22135         try {
22136             nodeRange.selectNode(node);
22137         } catch (e) {
22138             nodeRange.selectNodeContents(node);
22139         }
22140         
22141         
22142         range.collapse(true);
22143     
22144         nodeRange.collapse(true);
22145      
22146         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22147         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22148          
22149         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22150         
22151         var nodeIsBefore   =  ss == 1;
22152         var nodeIsAfter    = ee == -1;
22153         
22154         if (nodeIsBefore && nodeIsAfter) {
22155             return 0; // outer
22156         }
22157         if (!nodeIsBefore && nodeIsAfter) {
22158             return 1; //right trailed.
22159         }
22160         
22161         if (nodeIsBefore && !nodeIsAfter) {
22162             return 2;  // left trailed.
22163         }
22164         // fully contined.
22165         return 3;
22166     },
22167
22168     // private? - in a new class?
22169     cleanUpPaste :  function()
22170     {
22171         // cleans up the whole document..
22172         Roo.log('cleanuppaste');
22173         
22174         this.cleanUpChildren(this.doc.body);
22175         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22176         if (clean != this.doc.body.innerHTML) {
22177             this.doc.body.innerHTML = clean;
22178         }
22179         
22180     },
22181     
22182     cleanWordChars : function(input) {// change the chars to hex code
22183         var he = Roo.HtmlEditorCore;
22184         
22185         var output = input;
22186         Roo.each(he.swapCodes, function(sw) { 
22187             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22188             
22189             output = output.replace(swapper, sw[1]);
22190         });
22191         
22192         return output;
22193     },
22194     
22195     
22196     cleanUpChildren : function (n)
22197     {
22198         if (!n.childNodes.length) {
22199             return;
22200         }
22201         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22202            this.cleanUpChild(n.childNodes[i]);
22203         }
22204     },
22205     
22206     
22207         
22208     
22209     cleanUpChild : function (node)
22210     {
22211         var ed = this;
22212         //console.log(node);
22213         if (node.nodeName == "#text") {
22214             // clean up silly Windows -- stuff?
22215             return; 
22216         }
22217         if (node.nodeName == "#comment") {
22218             node.parentNode.removeChild(node);
22219             // clean up silly Windows -- stuff?
22220             return; 
22221         }
22222         var lcname = node.tagName.toLowerCase();
22223         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22224         // whitelist of tags..
22225         
22226         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22227             // remove node.
22228             node.parentNode.removeChild(node);
22229             return;
22230             
22231         }
22232         
22233         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22234         
22235         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22236         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22237         
22238         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22239         //    remove_keep_children = true;
22240         //}
22241         
22242         if (remove_keep_children) {
22243             this.cleanUpChildren(node);
22244             // inserts everything just before this node...
22245             while (node.childNodes.length) {
22246                 var cn = node.childNodes[0];
22247                 node.removeChild(cn);
22248                 node.parentNode.insertBefore(cn, node);
22249             }
22250             node.parentNode.removeChild(node);
22251             return;
22252         }
22253         
22254         if (!node.attributes || !node.attributes.length) {
22255             this.cleanUpChildren(node);
22256             return;
22257         }
22258         
22259         function cleanAttr(n,v)
22260         {
22261             
22262             if (v.match(/^\./) || v.match(/^\//)) {
22263                 return;
22264             }
22265             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22266                 return;
22267             }
22268             if (v.match(/^#/)) {
22269                 return;
22270             }
22271 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22272             node.removeAttribute(n);
22273             
22274         }
22275         
22276         var cwhite = this.cwhite;
22277         var cblack = this.cblack;
22278             
22279         function cleanStyle(n,v)
22280         {
22281             if (v.match(/expression/)) { //XSS?? should we even bother..
22282                 node.removeAttribute(n);
22283                 return;
22284             }
22285             
22286             var parts = v.split(/;/);
22287             var clean = [];
22288             
22289             Roo.each(parts, function(p) {
22290                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22291                 if (!p.length) {
22292                     return true;
22293                 }
22294                 var l = p.split(':').shift().replace(/\s+/g,'');
22295                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22296                 
22297                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22298 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22299                     //node.removeAttribute(n);
22300                     return true;
22301                 }
22302                 //Roo.log()
22303                 // only allow 'c whitelisted system attributes'
22304                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22305 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22306                     //node.removeAttribute(n);
22307                     return true;
22308                 }
22309                 
22310                 
22311                  
22312                 
22313                 clean.push(p);
22314                 return true;
22315             });
22316             if (clean.length) { 
22317                 node.setAttribute(n, clean.join(';'));
22318             } else {
22319                 node.removeAttribute(n);
22320             }
22321             
22322         }
22323         
22324         
22325         for (var i = node.attributes.length-1; i > -1 ; i--) {
22326             var a = node.attributes[i];
22327             //console.log(a);
22328             
22329             if (a.name.toLowerCase().substr(0,2)=='on')  {
22330                 node.removeAttribute(a.name);
22331                 continue;
22332             }
22333             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22334                 node.removeAttribute(a.name);
22335                 continue;
22336             }
22337             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22338                 cleanAttr(a.name,a.value); // fixme..
22339                 continue;
22340             }
22341             if (a.name == 'style') {
22342                 cleanStyle(a.name,a.value);
22343                 continue;
22344             }
22345             /// clean up MS crap..
22346             // tecnically this should be a list of valid class'es..
22347             
22348             
22349             if (a.name == 'class') {
22350                 if (a.value.match(/^Mso/)) {
22351                     node.className = '';
22352                 }
22353                 
22354                 if (a.value.match(/^body$/)) {
22355                     node.className = '';
22356                 }
22357                 continue;
22358             }
22359             
22360             // style cleanup!?
22361             // class cleanup?
22362             
22363         }
22364         
22365         
22366         this.cleanUpChildren(node);
22367         
22368         
22369     },
22370     
22371     /**
22372      * Clean up MS wordisms...
22373      */
22374     cleanWord : function(node)
22375     {
22376         
22377         
22378         if (!node) {
22379             this.cleanWord(this.doc.body);
22380             return;
22381         }
22382         if (node.nodeName == "#text") {
22383             // clean up silly Windows -- stuff?
22384             return; 
22385         }
22386         if (node.nodeName == "#comment") {
22387             node.parentNode.removeChild(node);
22388             // clean up silly Windows -- stuff?
22389             return; 
22390         }
22391         
22392         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22393             node.parentNode.removeChild(node);
22394             return;
22395         }
22396         
22397         // remove - but keep children..
22398         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22399             while (node.childNodes.length) {
22400                 var cn = node.childNodes[0];
22401                 node.removeChild(cn);
22402                 node.parentNode.insertBefore(cn, node);
22403             }
22404             node.parentNode.removeChild(node);
22405             this.iterateChildren(node, this.cleanWord);
22406             return;
22407         }
22408         // clean styles
22409         if (node.className.length) {
22410             
22411             var cn = node.className.split(/\W+/);
22412             var cna = [];
22413             Roo.each(cn, function(cls) {
22414                 if (cls.match(/Mso[a-zA-Z]+/)) {
22415                     return;
22416                 }
22417                 cna.push(cls);
22418             });
22419             node.className = cna.length ? cna.join(' ') : '';
22420             if (!cna.length) {
22421                 node.removeAttribute("class");
22422             }
22423         }
22424         
22425         if (node.hasAttribute("lang")) {
22426             node.removeAttribute("lang");
22427         }
22428         
22429         if (node.hasAttribute("style")) {
22430             
22431             var styles = node.getAttribute("style").split(";");
22432             var nstyle = [];
22433             Roo.each(styles, function(s) {
22434                 if (!s.match(/:/)) {
22435                     return;
22436                 }
22437                 var kv = s.split(":");
22438                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22439                     return;
22440                 }
22441                 // what ever is left... we allow.
22442                 nstyle.push(s);
22443             });
22444             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22445             if (!nstyle.length) {
22446                 node.removeAttribute('style');
22447             }
22448         }
22449         this.iterateChildren(node, this.cleanWord);
22450         
22451         
22452         
22453     },
22454     /**
22455      * iterateChildren of a Node, calling fn each time, using this as the scole..
22456      * @param {DomNode} node node to iterate children of.
22457      * @param {Function} fn method of this class to call on each item.
22458      */
22459     iterateChildren : function(node, fn)
22460     {
22461         if (!node.childNodes.length) {
22462                 return;
22463         }
22464         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22465            fn.call(this, node.childNodes[i])
22466         }
22467     },
22468     
22469     
22470     /**
22471      * cleanTableWidths.
22472      *
22473      * Quite often pasting from word etc.. results in tables with column and widths.
22474      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22475      *
22476      */
22477     cleanTableWidths : function(node)
22478     {
22479          
22480          
22481         if (!node) {
22482             this.cleanTableWidths(this.doc.body);
22483             return;
22484         }
22485         
22486         // ignore list...
22487         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22488             return; 
22489         }
22490         Roo.log(node.tagName);
22491         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22492             this.iterateChildren(node, this.cleanTableWidths);
22493             return;
22494         }
22495         if (node.hasAttribute('width')) {
22496             node.removeAttribute('width');
22497         }
22498         
22499          
22500         if (node.hasAttribute("style")) {
22501             // pretty basic...
22502             
22503             var styles = node.getAttribute("style").split(";");
22504             var nstyle = [];
22505             Roo.each(styles, function(s) {
22506                 if (!s.match(/:/)) {
22507                     return;
22508                 }
22509                 var kv = s.split(":");
22510                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22511                     return;
22512                 }
22513                 // what ever is left... we allow.
22514                 nstyle.push(s);
22515             });
22516             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22517             if (!nstyle.length) {
22518                 node.removeAttribute('style');
22519             }
22520         }
22521         
22522         this.iterateChildren(node, this.cleanTableWidths);
22523         
22524         
22525     },
22526     
22527     
22528     
22529     
22530     domToHTML : function(currentElement, depth, nopadtext) {
22531         
22532         depth = depth || 0;
22533         nopadtext = nopadtext || false;
22534     
22535         if (!currentElement) {
22536             return this.domToHTML(this.doc.body);
22537         }
22538         
22539         //Roo.log(currentElement);
22540         var j;
22541         var allText = false;
22542         var nodeName = currentElement.nodeName;
22543         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22544         
22545         if  (nodeName == '#text') {
22546             
22547             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22548         }
22549         
22550         
22551         var ret = '';
22552         if (nodeName != 'BODY') {
22553              
22554             var i = 0;
22555             // Prints the node tagName, such as <A>, <IMG>, etc
22556             if (tagName) {
22557                 var attr = [];
22558                 for(i = 0; i < currentElement.attributes.length;i++) {
22559                     // quoting?
22560                     var aname = currentElement.attributes.item(i).name;
22561                     if (!currentElement.attributes.item(i).value.length) {
22562                         continue;
22563                     }
22564                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22565                 }
22566                 
22567                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22568             } 
22569             else {
22570                 
22571                 // eack
22572             }
22573         } else {
22574             tagName = false;
22575         }
22576         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22577             return ret;
22578         }
22579         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22580             nopadtext = true;
22581         }
22582         
22583         
22584         // Traverse the tree
22585         i = 0;
22586         var currentElementChild = currentElement.childNodes.item(i);
22587         var allText = true;
22588         var innerHTML  = '';
22589         lastnode = '';
22590         while (currentElementChild) {
22591             // Formatting code (indent the tree so it looks nice on the screen)
22592             var nopad = nopadtext;
22593             if (lastnode == 'SPAN') {
22594                 nopad  = true;
22595             }
22596             // text
22597             if  (currentElementChild.nodeName == '#text') {
22598                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22599                 toadd = nopadtext ? toadd : toadd.trim();
22600                 if (!nopad && toadd.length > 80) {
22601                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22602                 }
22603                 innerHTML  += toadd;
22604                 
22605                 i++;
22606                 currentElementChild = currentElement.childNodes.item(i);
22607                 lastNode = '';
22608                 continue;
22609             }
22610             allText = false;
22611             
22612             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22613                 
22614             // Recursively traverse the tree structure of the child node
22615             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22616             lastnode = currentElementChild.nodeName;
22617             i++;
22618             currentElementChild=currentElement.childNodes.item(i);
22619         }
22620         
22621         ret += innerHTML;
22622         
22623         if (!allText) {
22624                 // The remaining code is mostly for formatting the tree
22625             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22626         }
22627         
22628         
22629         if (tagName) {
22630             ret+= "</"+tagName+">";
22631         }
22632         return ret;
22633         
22634     },
22635         
22636     applyBlacklists : function()
22637     {
22638         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22639         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22640         
22641         this.white = [];
22642         this.black = [];
22643         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22644             if (b.indexOf(tag) > -1) {
22645                 return;
22646             }
22647             this.white.push(tag);
22648             
22649         }, this);
22650         
22651         Roo.each(w, function(tag) {
22652             if (b.indexOf(tag) > -1) {
22653                 return;
22654             }
22655             if (this.white.indexOf(tag) > -1) {
22656                 return;
22657             }
22658             this.white.push(tag);
22659             
22660         }, this);
22661         
22662         
22663         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22664             if (w.indexOf(tag) > -1) {
22665                 return;
22666             }
22667             this.black.push(tag);
22668             
22669         }, this);
22670         
22671         Roo.each(b, function(tag) {
22672             if (w.indexOf(tag) > -1) {
22673                 return;
22674             }
22675             if (this.black.indexOf(tag) > -1) {
22676                 return;
22677             }
22678             this.black.push(tag);
22679             
22680         }, this);
22681         
22682         
22683         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22684         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22685         
22686         this.cwhite = [];
22687         this.cblack = [];
22688         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22689             if (b.indexOf(tag) > -1) {
22690                 return;
22691             }
22692             this.cwhite.push(tag);
22693             
22694         }, this);
22695         
22696         Roo.each(w, function(tag) {
22697             if (b.indexOf(tag) > -1) {
22698                 return;
22699             }
22700             if (this.cwhite.indexOf(tag) > -1) {
22701                 return;
22702             }
22703             this.cwhite.push(tag);
22704             
22705         }, this);
22706         
22707         
22708         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22709             if (w.indexOf(tag) > -1) {
22710                 return;
22711             }
22712             this.cblack.push(tag);
22713             
22714         }, this);
22715         
22716         Roo.each(b, function(tag) {
22717             if (w.indexOf(tag) > -1) {
22718                 return;
22719             }
22720             if (this.cblack.indexOf(tag) > -1) {
22721                 return;
22722             }
22723             this.cblack.push(tag);
22724             
22725         }, this);
22726     },
22727     
22728     setStylesheets : function(stylesheets)
22729     {
22730         if(typeof(stylesheets) == 'string'){
22731             Roo.get(this.iframe.contentDocument.head).createChild({
22732                 tag : 'link',
22733                 rel : 'stylesheet',
22734                 type : 'text/css',
22735                 href : stylesheets
22736             });
22737             
22738             return;
22739         }
22740         var _this = this;
22741      
22742         Roo.each(stylesheets, function(s) {
22743             if(!s.length){
22744                 return;
22745             }
22746             
22747             Roo.get(_this.iframe.contentDocument.head).createChild({
22748                 tag : 'link',
22749                 rel : 'stylesheet',
22750                 type : 'text/css',
22751                 href : s
22752             });
22753         });
22754
22755         
22756     },
22757     
22758     removeStylesheets : function()
22759     {
22760         var _this = this;
22761         
22762         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22763             s.remove();
22764         });
22765     },
22766     
22767     setStyle : function(style)
22768     {
22769         Roo.get(this.iframe.contentDocument.head).createChild({
22770             tag : 'style',
22771             type : 'text/css',
22772             html : style
22773         });
22774
22775         return;
22776     }
22777     
22778     // hide stuff that is not compatible
22779     /**
22780      * @event blur
22781      * @hide
22782      */
22783     /**
22784      * @event change
22785      * @hide
22786      */
22787     /**
22788      * @event focus
22789      * @hide
22790      */
22791     /**
22792      * @event specialkey
22793      * @hide
22794      */
22795     /**
22796      * @cfg {String} fieldClass @hide
22797      */
22798     /**
22799      * @cfg {String} focusClass @hide
22800      */
22801     /**
22802      * @cfg {String} autoCreate @hide
22803      */
22804     /**
22805      * @cfg {String} inputType @hide
22806      */
22807     /**
22808      * @cfg {String} invalidClass @hide
22809      */
22810     /**
22811      * @cfg {String} invalidText @hide
22812      */
22813     /**
22814      * @cfg {String} msgFx @hide
22815      */
22816     /**
22817      * @cfg {String} validateOnBlur @hide
22818      */
22819 });
22820
22821 Roo.HtmlEditorCore.white = [
22822         'area', 'br', 'img', 'input', 'hr', 'wbr',
22823         
22824        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22825        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22826        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22827        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22828        'table',   'ul',         'xmp', 
22829        
22830        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22831       'thead',   'tr', 
22832      
22833       'dir', 'menu', 'ol', 'ul', 'dl',
22834        
22835       'embed',  'object'
22836 ];
22837
22838
22839 Roo.HtmlEditorCore.black = [
22840     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22841         'applet', // 
22842         'base',   'basefont', 'bgsound', 'blink',  'body', 
22843         'frame',  'frameset', 'head',    'html',   'ilayer', 
22844         'iframe', 'layer',  'link',     'meta',    'object',   
22845         'script', 'style' ,'title',  'xml' // clean later..
22846 ];
22847 Roo.HtmlEditorCore.clean = [
22848     'script', 'style', 'title', 'xml'
22849 ];
22850 Roo.HtmlEditorCore.remove = [
22851     'font'
22852 ];
22853 // attributes..
22854
22855 Roo.HtmlEditorCore.ablack = [
22856     'on'
22857 ];
22858     
22859 Roo.HtmlEditorCore.aclean = [ 
22860     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22861 ];
22862
22863 // protocols..
22864 Roo.HtmlEditorCore.pwhite= [
22865         'http',  'https',  'mailto'
22866 ];
22867
22868 // white listed style attributes.
22869 Roo.HtmlEditorCore.cwhite= [
22870       //  'text-align', /// default is to allow most things..
22871       
22872          
22873 //        'font-size'//??
22874 ];
22875
22876 // black listed style attributes.
22877 Roo.HtmlEditorCore.cblack= [
22878       //  'font-size' -- this can be set by the project 
22879 ];
22880
22881
22882 Roo.HtmlEditorCore.swapCodes   =[ 
22883     [    8211, "--" ], 
22884     [    8212, "--" ], 
22885     [    8216,  "'" ],  
22886     [    8217, "'" ],  
22887     [    8220, '"' ],  
22888     [    8221, '"' ],  
22889     [    8226, "*" ],  
22890     [    8230, "..." ]
22891 ]; 
22892
22893     /*
22894  * - LGPL
22895  *
22896  * HtmlEditor
22897  * 
22898  */
22899
22900 /**
22901  * @class Roo.bootstrap.HtmlEditor
22902  * @extends Roo.bootstrap.TextArea
22903  * Bootstrap HtmlEditor class
22904
22905  * @constructor
22906  * Create a new HtmlEditor
22907  * @param {Object} config The config object
22908  */
22909
22910 Roo.bootstrap.HtmlEditor = function(config){
22911     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22912     if (!this.toolbars) {
22913         this.toolbars = [];
22914     }
22915     
22916     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22917     this.addEvents({
22918             /**
22919              * @event initialize
22920              * Fires when the editor is fully initialized (including the iframe)
22921              * @param {HtmlEditor} this
22922              */
22923             initialize: true,
22924             /**
22925              * @event activate
22926              * Fires when the editor is first receives the focus. Any insertion must wait
22927              * until after this event.
22928              * @param {HtmlEditor} this
22929              */
22930             activate: true,
22931              /**
22932              * @event beforesync
22933              * Fires before the textarea is updated with content from the editor iframe. Return false
22934              * to cancel the sync.
22935              * @param {HtmlEditor} this
22936              * @param {String} html
22937              */
22938             beforesync: true,
22939              /**
22940              * @event beforepush
22941              * Fires before the iframe editor is updated with content from the textarea. Return false
22942              * to cancel the push.
22943              * @param {HtmlEditor} this
22944              * @param {String} html
22945              */
22946             beforepush: true,
22947              /**
22948              * @event sync
22949              * Fires when the textarea is updated with content from the editor iframe.
22950              * @param {HtmlEditor} this
22951              * @param {String} html
22952              */
22953             sync: true,
22954              /**
22955              * @event push
22956              * Fires when the iframe editor is updated with content from the textarea.
22957              * @param {HtmlEditor} this
22958              * @param {String} html
22959              */
22960             push: true,
22961              /**
22962              * @event editmodechange
22963              * Fires when the editor switches edit modes
22964              * @param {HtmlEditor} this
22965              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22966              */
22967             editmodechange: true,
22968             /**
22969              * @event editorevent
22970              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22971              * @param {HtmlEditor} this
22972              */
22973             editorevent: true,
22974             /**
22975              * @event firstfocus
22976              * Fires when on first focus - needed by toolbars..
22977              * @param {HtmlEditor} this
22978              */
22979             firstfocus: true,
22980             /**
22981              * @event autosave
22982              * Auto save the htmlEditor value as a file into Events
22983              * @param {HtmlEditor} this
22984              */
22985             autosave: true,
22986             /**
22987              * @event savedpreview
22988              * preview the saved version of htmlEditor
22989              * @param {HtmlEditor} this
22990              */
22991             savedpreview: true
22992         });
22993 };
22994
22995
22996 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22997     
22998     
22999       /**
23000      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23001      */
23002     toolbars : false,
23003     
23004      /**
23005     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23006     */
23007     btns : [],
23008    
23009      /**
23010      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23011      *                        Roo.resizable.
23012      */
23013     resizable : false,
23014      /**
23015      * @cfg {Number} height (in pixels)
23016      */   
23017     height: 300,
23018    /**
23019      * @cfg {Number} width (in pixels)
23020      */   
23021     width: false,
23022     
23023     /**
23024      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23025      * 
23026      */
23027     stylesheets: false,
23028     
23029     // id of frame..
23030     frameId: false,
23031     
23032     // private properties
23033     validationEvent : false,
23034     deferHeight: true,
23035     initialized : false,
23036     activated : false,
23037     
23038     onFocus : Roo.emptyFn,
23039     iframePad:3,
23040     hideMode:'offsets',
23041     
23042     tbContainer : false,
23043     
23044     bodyCls : '',
23045     
23046     toolbarContainer :function() {
23047         return this.wrap.select('.x-html-editor-tb',true).first();
23048     },
23049
23050     /**
23051      * Protected method that will not generally be called directly. It
23052      * is called when the editor creates its toolbar. Override this method if you need to
23053      * add custom toolbar buttons.
23054      * @param {HtmlEditor} editor
23055      */
23056     createToolbar : function(){
23057         Roo.log('renewing');
23058         Roo.log("create toolbars");
23059         
23060         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23061         this.toolbars[0].render(this.toolbarContainer());
23062         
23063         return;
23064         
23065 //        if (!editor.toolbars || !editor.toolbars.length) {
23066 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23067 //        }
23068 //        
23069 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23070 //            editor.toolbars[i] = Roo.factory(
23071 //                    typeof(editor.toolbars[i]) == 'string' ?
23072 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23073 //                Roo.bootstrap.HtmlEditor);
23074 //            editor.toolbars[i].init(editor);
23075 //        }
23076     },
23077
23078      
23079     // private
23080     onRender : function(ct, position)
23081     {
23082        // Roo.log("Call onRender: " + this.xtype);
23083         var _t = this;
23084         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23085       
23086         this.wrap = this.inputEl().wrap({
23087             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23088         });
23089         
23090         this.editorcore.onRender(ct, position);
23091          
23092         if (this.resizable) {
23093             this.resizeEl = new Roo.Resizable(this.wrap, {
23094                 pinned : true,
23095                 wrap: true,
23096                 dynamic : true,
23097                 minHeight : this.height,
23098                 height: this.height,
23099                 handles : this.resizable,
23100                 width: this.width,
23101                 listeners : {
23102                     resize : function(r, w, h) {
23103                         _t.onResize(w,h); // -something
23104                     }
23105                 }
23106             });
23107             
23108         }
23109         this.createToolbar(this);
23110        
23111         
23112         if(!this.width && this.resizable){
23113             this.setSize(this.wrap.getSize());
23114         }
23115         if (this.resizeEl) {
23116             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23117             // should trigger onReize..
23118         }
23119         
23120     },
23121
23122     // private
23123     onResize : function(w, h)
23124     {
23125         Roo.log('resize: ' +w + ',' + h );
23126         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23127         var ew = false;
23128         var eh = false;
23129         
23130         if(this.inputEl() ){
23131             if(typeof w == 'number'){
23132                 var aw = w - this.wrap.getFrameWidth('lr');
23133                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23134                 ew = aw;
23135             }
23136             if(typeof h == 'number'){
23137                  var tbh = -11;  // fixme it needs to tool bar size!
23138                 for (var i =0; i < this.toolbars.length;i++) {
23139                     // fixme - ask toolbars for heights?
23140                     tbh += this.toolbars[i].el.getHeight();
23141                     //if (this.toolbars[i].footer) {
23142                     //    tbh += this.toolbars[i].footer.el.getHeight();
23143                     //}
23144                 }
23145               
23146                 
23147                 
23148                 
23149                 
23150                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23151                 ah -= 5; // knock a few pixes off for look..
23152                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23153                 var eh = ah;
23154             }
23155         }
23156         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23157         this.editorcore.onResize(ew,eh);
23158         
23159     },
23160
23161     /**
23162      * Toggles the editor between standard and source edit mode.
23163      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23164      */
23165     toggleSourceEdit : function(sourceEditMode)
23166     {
23167         this.editorcore.toggleSourceEdit(sourceEditMode);
23168         
23169         if(this.editorcore.sourceEditMode){
23170             Roo.log('editor - showing textarea');
23171             
23172 //            Roo.log('in');
23173 //            Roo.log(this.syncValue());
23174             this.syncValue();
23175             this.inputEl().removeClass(['hide', 'x-hidden']);
23176             this.inputEl().dom.removeAttribute('tabIndex');
23177             this.inputEl().focus();
23178         }else{
23179             Roo.log('editor - hiding textarea');
23180 //            Roo.log('out')
23181 //            Roo.log(this.pushValue()); 
23182             this.pushValue();
23183             
23184             this.inputEl().addClass(['hide', 'x-hidden']);
23185             this.inputEl().dom.setAttribute('tabIndex', -1);
23186             //this.deferFocus();
23187         }
23188          
23189         if(this.resizable){
23190             this.setSize(this.wrap.getSize());
23191         }
23192         
23193         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23194     },
23195  
23196     // private (for BoxComponent)
23197     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23198
23199     // private (for BoxComponent)
23200     getResizeEl : function(){
23201         return this.wrap;
23202     },
23203
23204     // private (for BoxComponent)
23205     getPositionEl : function(){
23206         return this.wrap;
23207     },
23208
23209     // private
23210     initEvents : function(){
23211         this.originalValue = this.getValue();
23212     },
23213
23214 //    /**
23215 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23216 //     * @method
23217 //     */
23218 //    markInvalid : Roo.emptyFn,
23219 //    /**
23220 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23221 //     * @method
23222 //     */
23223 //    clearInvalid : Roo.emptyFn,
23224
23225     setValue : function(v){
23226         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23227         this.editorcore.pushValue();
23228     },
23229
23230      
23231     // private
23232     deferFocus : function(){
23233         this.focus.defer(10, this);
23234     },
23235
23236     // doc'ed in Field
23237     focus : function(){
23238         this.editorcore.focus();
23239         
23240     },
23241       
23242
23243     // private
23244     onDestroy : function(){
23245         
23246         
23247         
23248         if(this.rendered){
23249             
23250             for (var i =0; i < this.toolbars.length;i++) {
23251                 // fixme - ask toolbars for heights?
23252                 this.toolbars[i].onDestroy();
23253             }
23254             
23255             this.wrap.dom.innerHTML = '';
23256             this.wrap.remove();
23257         }
23258     },
23259
23260     // private
23261     onFirstFocus : function(){
23262         //Roo.log("onFirstFocus");
23263         this.editorcore.onFirstFocus();
23264          for (var i =0; i < this.toolbars.length;i++) {
23265             this.toolbars[i].onFirstFocus();
23266         }
23267         
23268     },
23269     
23270     // private
23271     syncValue : function()
23272     {   
23273         this.editorcore.syncValue();
23274     },
23275     
23276     pushValue : function()
23277     {   
23278         this.editorcore.pushValue();
23279     }
23280      
23281     
23282     // hide stuff that is not compatible
23283     /**
23284      * @event blur
23285      * @hide
23286      */
23287     /**
23288      * @event change
23289      * @hide
23290      */
23291     /**
23292      * @event focus
23293      * @hide
23294      */
23295     /**
23296      * @event specialkey
23297      * @hide
23298      */
23299     /**
23300      * @cfg {String} fieldClass @hide
23301      */
23302     /**
23303      * @cfg {String} focusClass @hide
23304      */
23305     /**
23306      * @cfg {String} autoCreate @hide
23307      */
23308     /**
23309      * @cfg {String} inputType @hide
23310      */
23311     /**
23312      * @cfg {String} invalidClass @hide
23313      */
23314     /**
23315      * @cfg {String} invalidText @hide
23316      */
23317     /**
23318      * @cfg {String} msgFx @hide
23319      */
23320     /**
23321      * @cfg {String} validateOnBlur @hide
23322      */
23323 });
23324  
23325     
23326    
23327    
23328    
23329       
23330 Roo.namespace('Roo.bootstrap.htmleditor');
23331 /**
23332  * @class Roo.bootstrap.HtmlEditorToolbar1
23333  * Basic Toolbar
23334  * 
23335  * Usage:
23336  *
23337  new Roo.bootstrap.HtmlEditor({
23338     ....
23339     toolbars : [
23340         new Roo.bootstrap.HtmlEditorToolbar1({
23341             disable : { fonts: 1 , format: 1, ..., ... , ...],
23342             btns : [ .... ]
23343         })
23344     }
23345      
23346  * 
23347  * @cfg {Object} disable List of elements to disable..
23348  * @cfg {Array} btns List of additional buttons.
23349  * 
23350  * 
23351  * NEEDS Extra CSS? 
23352  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23353  */
23354  
23355 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23356 {
23357     
23358     Roo.apply(this, config);
23359     
23360     // default disabled, based on 'good practice'..
23361     this.disable = this.disable || {};
23362     Roo.applyIf(this.disable, {
23363         fontSize : true,
23364         colors : true,
23365         specialElements : true
23366     });
23367     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23368     
23369     this.editor = config.editor;
23370     this.editorcore = config.editor.editorcore;
23371     
23372     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23373     
23374     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23375     // dont call parent... till later.
23376 }
23377 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23378      
23379     bar : true,
23380     
23381     editor : false,
23382     editorcore : false,
23383     
23384     
23385     formats : [
23386         "p" ,  
23387         "h1","h2","h3","h4","h5","h6", 
23388         "pre", "code", 
23389         "abbr", "acronym", "address", "cite", "samp", "var",
23390         'div','span'
23391     ],
23392     
23393     onRender : function(ct, position)
23394     {
23395        // Roo.log("Call onRender: " + this.xtype);
23396         
23397        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23398        Roo.log(this.el);
23399        this.el.dom.style.marginBottom = '0';
23400        var _this = this;
23401        var editorcore = this.editorcore;
23402        var editor= this.editor;
23403        
23404        var children = [];
23405        var btn = function(id,cmd , toggle, handler, html){
23406        
23407             var  event = toggle ? 'toggle' : 'click';
23408        
23409             var a = {
23410                 size : 'sm',
23411                 xtype: 'Button',
23412                 xns: Roo.bootstrap,
23413                 glyphicon : id,
23414                 cmd : id || cmd,
23415                 enableToggle:toggle !== false,
23416                 html : html || '',
23417                 pressed : toggle ? false : null,
23418                 listeners : {}
23419             };
23420             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23421                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23422             };
23423             children.push(a);
23424             return a;
23425        }
23426        
23427     //    var cb_box = function...
23428         
23429         var style = {
23430                 xtype: 'Button',
23431                 size : 'sm',
23432                 xns: Roo.bootstrap,
23433                 glyphicon : 'font',
23434                 //html : 'submit'
23435                 menu : {
23436                     xtype: 'Menu',
23437                     xns: Roo.bootstrap,
23438                     items:  []
23439                 }
23440         };
23441         Roo.each(this.formats, function(f) {
23442             style.menu.items.push({
23443                 xtype :'MenuItem',
23444                 xns: Roo.bootstrap,
23445                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23446                 tagname : f,
23447                 listeners : {
23448                     click : function()
23449                     {
23450                         editorcore.insertTag(this.tagname);
23451                         editor.focus();
23452                     }
23453                 }
23454                 
23455             });
23456         });
23457         children.push(style);   
23458         
23459         btn('bold',false,true);
23460         btn('italic',false,true);
23461         btn('align-left', 'justifyleft',true);
23462         btn('align-center', 'justifycenter',true);
23463         btn('align-right' , 'justifyright',true);
23464         btn('link', false, false, function(btn) {
23465             //Roo.log("create link?");
23466             var url = prompt(this.createLinkText, this.defaultLinkValue);
23467             if(url && url != 'http:/'+'/'){
23468                 this.editorcore.relayCmd('createlink', url);
23469             }
23470         }),
23471         btn('list','insertunorderedlist',true);
23472         btn('pencil', false,true, function(btn){
23473                 Roo.log(this);
23474                 this.toggleSourceEdit(btn.pressed);
23475         });
23476         
23477         if (this.editor.btns.length > 0) {
23478             for (var i = 0; i<this.editor.btns.length; i++) {
23479                 children.push(this.editor.btns[i]);
23480             }
23481         }
23482         
23483         /*
23484         var cog = {
23485                 xtype: 'Button',
23486                 size : 'sm',
23487                 xns: Roo.bootstrap,
23488                 glyphicon : 'cog',
23489                 //html : 'submit'
23490                 menu : {
23491                     xtype: 'Menu',
23492                     xns: Roo.bootstrap,
23493                     items:  []
23494                 }
23495         };
23496         
23497         cog.menu.items.push({
23498             xtype :'MenuItem',
23499             xns: Roo.bootstrap,
23500             html : Clean styles,
23501             tagname : f,
23502             listeners : {
23503                 click : function()
23504                 {
23505                     editorcore.insertTag(this.tagname);
23506                     editor.focus();
23507                 }
23508             }
23509             
23510         });
23511        */
23512         
23513          
23514        this.xtype = 'NavSimplebar';
23515         
23516         for(var i=0;i< children.length;i++) {
23517             
23518             this.buttons.add(this.addxtypeChild(children[i]));
23519             
23520         }
23521         
23522         editor.on('editorevent', this.updateToolbar, this);
23523     },
23524     onBtnClick : function(id)
23525     {
23526        this.editorcore.relayCmd(id);
23527        this.editorcore.focus();
23528     },
23529     
23530     /**
23531      * Protected method that will not generally be called directly. It triggers
23532      * a toolbar update by reading the markup state of the current selection in the editor.
23533      */
23534     updateToolbar: function(){
23535
23536         if(!this.editorcore.activated){
23537             this.editor.onFirstFocus(); // is this neeed?
23538             return;
23539         }
23540
23541         var btns = this.buttons; 
23542         var doc = this.editorcore.doc;
23543         btns.get('bold').setActive(doc.queryCommandState('bold'));
23544         btns.get('italic').setActive(doc.queryCommandState('italic'));
23545         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23546         
23547         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23548         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23549         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23550         
23551         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23552         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23553          /*
23554         
23555         var ans = this.editorcore.getAllAncestors();
23556         if (this.formatCombo) {
23557             
23558             
23559             var store = this.formatCombo.store;
23560             this.formatCombo.setValue("");
23561             for (var i =0; i < ans.length;i++) {
23562                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23563                     // select it..
23564                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23565                     break;
23566                 }
23567             }
23568         }
23569         
23570         
23571         
23572         // hides menus... - so this cant be on a menu...
23573         Roo.bootstrap.MenuMgr.hideAll();
23574         */
23575         Roo.bootstrap.MenuMgr.hideAll();
23576         //this.editorsyncValue();
23577     },
23578     onFirstFocus: function() {
23579         this.buttons.each(function(item){
23580            item.enable();
23581         });
23582     },
23583     toggleSourceEdit : function(sourceEditMode){
23584         
23585           
23586         if(sourceEditMode){
23587             Roo.log("disabling buttons");
23588            this.buttons.each( function(item){
23589                 if(item.cmd != 'pencil'){
23590                     item.disable();
23591                 }
23592             });
23593           
23594         }else{
23595             Roo.log("enabling buttons");
23596             if(this.editorcore.initialized){
23597                 this.buttons.each( function(item){
23598                     item.enable();
23599                 });
23600             }
23601             
23602         }
23603         Roo.log("calling toggole on editor");
23604         // tell the editor that it's been pressed..
23605         this.editor.toggleSourceEdit(sourceEditMode);
23606        
23607     }
23608 });
23609
23610
23611
23612
23613
23614 /**
23615  * @class Roo.bootstrap.Table.AbstractSelectionModel
23616  * @extends Roo.util.Observable
23617  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23618  * implemented by descendant classes.  This class should not be directly instantiated.
23619  * @constructor
23620  */
23621 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23622     this.locked = false;
23623     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23624 };
23625
23626
23627 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23628     /** @ignore Called by the grid automatically. Do not call directly. */
23629     init : function(grid){
23630         this.grid = grid;
23631         this.initEvents();
23632     },
23633
23634     /**
23635      * Locks the selections.
23636      */
23637     lock : function(){
23638         this.locked = true;
23639     },
23640
23641     /**
23642      * Unlocks the selections.
23643      */
23644     unlock : function(){
23645         this.locked = false;
23646     },
23647
23648     /**
23649      * Returns true if the selections are locked.
23650      * @return {Boolean}
23651      */
23652     isLocked : function(){
23653         return this.locked;
23654     }
23655 });
23656 /**
23657  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23658  * @class Roo.bootstrap.Table.RowSelectionModel
23659  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23660  * It supports multiple selections and keyboard selection/navigation. 
23661  * @constructor
23662  * @param {Object} config
23663  */
23664
23665 Roo.bootstrap.Table.RowSelectionModel = function(config){
23666     Roo.apply(this, config);
23667     this.selections = new Roo.util.MixedCollection(false, function(o){
23668         return o.id;
23669     });
23670
23671     this.last = false;
23672     this.lastActive = false;
23673
23674     this.addEvents({
23675         /**
23676              * @event selectionchange
23677              * Fires when the selection changes
23678              * @param {SelectionModel} this
23679              */
23680             "selectionchange" : true,
23681         /**
23682              * @event afterselectionchange
23683              * Fires after the selection changes (eg. by key press or clicking)
23684              * @param {SelectionModel} this
23685              */
23686             "afterselectionchange" : true,
23687         /**
23688              * @event beforerowselect
23689              * Fires when a row is selected being selected, return false to cancel.
23690              * @param {SelectionModel} this
23691              * @param {Number} rowIndex The selected index
23692              * @param {Boolean} keepExisting False if other selections will be cleared
23693              */
23694             "beforerowselect" : true,
23695         /**
23696              * @event rowselect
23697              * Fires when a row is selected.
23698              * @param {SelectionModel} this
23699              * @param {Number} rowIndex The selected index
23700              * @param {Roo.data.Record} r The record
23701              */
23702             "rowselect" : true,
23703         /**
23704              * @event rowdeselect
23705              * Fires when a row is deselected.
23706              * @param {SelectionModel} this
23707              * @param {Number} rowIndex The selected index
23708              */
23709         "rowdeselect" : true
23710     });
23711     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23712     this.locked = false;
23713  };
23714
23715 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23716     /**
23717      * @cfg {Boolean} singleSelect
23718      * True to allow selection of only one row at a time (defaults to false)
23719      */
23720     singleSelect : false,
23721
23722     // private
23723     initEvents : function()
23724     {
23725
23726         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23727         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23728         //}else{ // allow click to work like normal
23729          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23730         //}
23731         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23732         this.grid.on("rowclick", this.handleMouseDown, this);
23733         
23734         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23735             "up" : function(e){
23736                 if(!e.shiftKey){
23737                     this.selectPrevious(e.shiftKey);
23738                 }else if(this.last !== false && this.lastActive !== false){
23739                     var last = this.last;
23740                     this.selectRange(this.last,  this.lastActive-1);
23741                     this.grid.getView().focusRow(this.lastActive);
23742                     if(last !== false){
23743                         this.last = last;
23744                     }
23745                 }else{
23746                     this.selectFirstRow();
23747                 }
23748                 this.fireEvent("afterselectionchange", this);
23749             },
23750             "down" : function(e){
23751                 if(!e.shiftKey){
23752                     this.selectNext(e.shiftKey);
23753                 }else if(this.last !== false && this.lastActive !== false){
23754                     var last = this.last;
23755                     this.selectRange(this.last,  this.lastActive+1);
23756                     this.grid.getView().focusRow(this.lastActive);
23757                     if(last !== false){
23758                         this.last = last;
23759                     }
23760                 }else{
23761                     this.selectFirstRow();
23762                 }
23763                 this.fireEvent("afterselectionchange", this);
23764             },
23765             scope: this
23766         });
23767         this.grid.store.on('load', function(){
23768             this.selections.clear();
23769         },this);
23770         /*
23771         var view = this.grid.view;
23772         view.on("refresh", this.onRefresh, this);
23773         view.on("rowupdated", this.onRowUpdated, this);
23774         view.on("rowremoved", this.onRemove, this);
23775         */
23776     },
23777
23778     // private
23779     onRefresh : function()
23780     {
23781         var ds = this.grid.store, i, v = this.grid.view;
23782         var s = this.selections;
23783         s.each(function(r){
23784             if((i = ds.indexOfId(r.id)) != -1){
23785                 v.onRowSelect(i);
23786             }else{
23787                 s.remove(r);
23788             }
23789         });
23790     },
23791
23792     // private
23793     onRemove : function(v, index, r){
23794         this.selections.remove(r);
23795     },
23796
23797     // private
23798     onRowUpdated : function(v, index, r){
23799         if(this.isSelected(r)){
23800             v.onRowSelect(index);
23801         }
23802     },
23803
23804     /**
23805      * Select records.
23806      * @param {Array} records The records to select
23807      * @param {Boolean} keepExisting (optional) True to keep existing selections
23808      */
23809     selectRecords : function(records, keepExisting)
23810     {
23811         if(!keepExisting){
23812             this.clearSelections();
23813         }
23814             var ds = this.grid.store;
23815         for(var i = 0, len = records.length; i < len; i++){
23816             this.selectRow(ds.indexOf(records[i]), true);
23817         }
23818     },
23819
23820     /**
23821      * Gets the number of selected rows.
23822      * @return {Number}
23823      */
23824     getCount : function(){
23825         return this.selections.length;
23826     },
23827
23828     /**
23829      * Selects the first row in the grid.
23830      */
23831     selectFirstRow : function(){
23832         this.selectRow(0);
23833     },
23834
23835     /**
23836      * Select the last row.
23837      * @param {Boolean} keepExisting (optional) True to keep existing selections
23838      */
23839     selectLastRow : function(keepExisting){
23840         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23841         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23842     },
23843
23844     /**
23845      * Selects the row immediately following the last selected row.
23846      * @param {Boolean} keepExisting (optional) True to keep existing selections
23847      */
23848     selectNext : function(keepExisting)
23849     {
23850             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23851             this.selectRow(this.last+1, keepExisting);
23852             this.grid.getView().focusRow(this.last);
23853         }
23854     },
23855
23856     /**
23857      * Selects the row that precedes the last selected row.
23858      * @param {Boolean} keepExisting (optional) True to keep existing selections
23859      */
23860     selectPrevious : function(keepExisting){
23861         if(this.last){
23862             this.selectRow(this.last-1, keepExisting);
23863             this.grid.getView().focusRow(this.last);
23864         }
23865     },
23866
23867     /**
23868      * Returns the selected records
23869      * @return {Array} Array of selected records
23870      */
23871     getSelections : function(){
23872         return [].concat(this.selections.items);
23873     },
23874
23875     /**
23876      * Returns the first selected record.
23877      * @return {Record}
23878      */
23879     getSelected : function(){
23880         return this.selections.itemAt(0);
23881     },
23882
23883
23884     /**
23885      * Clears all selections.
23886      */
23887     clearSelections : function(fast)
23888     {
23889         if(this.locked) {
23890             return;
23891         }
23892         if(fast !== true){
23893                 var ds = this.grid.store;
23894             var s = this.selections;
23895             s.each(function(r){
23896                 this.deselectRow(ds.indexOfId(r.id));
23897             }, this);
23898             s.clear();
23899         }else{
23900             this.selections.clear();
23901         }
23902         this.last = false;
23903     },
23904
23905
23906     /**
23907      * Selects all rows.
23908      */
23909     selectAll : function(){
23910         if(this.locked) {
23911             return;
23912         }
23913         this.selections.clear();
23914         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23915             this.selectRow(i, true);
23916         }
23917     },
23918
23919     /**
23920      * Returns True if there is a selection.
23921      * @return {Boolean}
23922      */
23923     hasSelection : function(){
23924         return this.selections.length > 0;
23925     },
23926
23927     /**
23928      * Returns True if the specified row is selected.
23929      * @param {Number/Record} record The record or index of the record to check
23930      * @return {Boolean}
23931      */
23932     isSelected : function(index){
23933             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23934         return (r && this.selections.key(r.id) ? true : false);
23935     },
23936
23937     /**
23938      * Returns True if the specified record id is selected.
23939      * @param {String} id The id of record to check
23940      * @return {Boolean}
23941      */
23942     isIdSelected : function(id){
23943         return (this.selections.key(id) ? true : false);
23944     },
23945
23946
23947     // private
23948     handleMouseDBClick : function(e, t){
23949         
23950     },
23951     // private
23952     handleMouseDown : function(e, t)
23953     {
23954             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23955         if(this.isLocked() || rowIndex < 0 ){
23956             return;
23957         };
23958         if(e.shiftKey && this.last !== false){
23959             var last = this.last;
23960             this.selectRange(last, rowIndex, e.ctrlKey);
23961             this.last = last; // reset the last
23962             t.focus();
23963     
23964         }else{
23965             var isSelected = this.isSelected(rowIndex);
23966             //Roo.log("select row:" + rowIndex);
23967             if(isSelected){
23968                 this.deselectRow(rowIndex);
23969             } else {
23970                         this.selectRow(rowIndex, true);
23971             }
23972     
23973             /*
23974                 if(e.button !== 0 && isSelected){
23975                 alert('rowIndex 2: ' + rowIndex);
23976                     view.focusRow(rowIndex);
23977                 }else if(e.ctrlKey && isSelected){
23978                     this.deselectRow(rowIndex);
23979                 }else if(!isSelected){
23980                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23981                     view.focusRow(rowIndex);
23982                 }
23983             */
23984         }
23985         this.fireEvent("afterselectionchange", this);
23986     },
23987     // private
23988     handleDragableRowClick :  function(grid, rowIndex, e) 
23989     {
23990         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23991             this.selectRow(rowIndex, false);
23992             grid.view.focusRow(rowIndex);
23993              this.fireEvent("afterselectionchange", this);
23994         }
23995     },
23996     
23997     /**
23998      * Selects multiple rows.
23999      * @param {Array} rows Array of the indexes of the row to select
24000      * @param {Boolean} keepExisting (optional) True to keep existing selections
24001      */
24002     selectRows : function(rows, keepExisting){
24003         if(!keepExisting){
24004             this.clearSelections();
24005         }
24006         for(var i = 0, len = rows.length; i < len; i++){
24007             this.selectRow(rows[i], true);
24008         }
24009     },
24010
24011     /**
24012      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24013      * @param {Number} startRow The index of the first row in the range
24014      * @param {Number} endRow The index of the last row in the range
24015      * @param {Boolean} keepExisting (optional) True to retain existing selections
24016      */
24017     selectRange : function(startRow, endRow, keepExisting){
24018         if(this.locked) {
24019             return;
24020         }
24021         if(!keepExisting){
24022             this.clearSelections();
24023         }
24024         if(startRow <= endRow){
24025             for(var i = startRow; i <= endRow; i++){
24026                 this.selectRow(i, true);
24027             }
24028         }else{
24029             for(var i = startRow; i >= endRow; i--){
24030                 this.selectRow(i, true);
24031             }
24032         }
24033     },
24034
24035     /**
24036      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24037      * @param {Number} startRow The index of the first row in the range
24038      * @param {Number} endRow The index of the last row in the range
24039      */
24040     deselectRange : function(startRow, endRow, preventViewNotify){
24041         if(this.locked) {
24042             return;
24043         }
24044         for(var i = startRow; i <= endRow; i++){
24045             this.deselectRow(i, preventViewNotify);
24046         }
24047     },
24048
24049     /**
24050      * Selects a row.
24051      * @param {Number} row The index of the row to select
24052      * @param {Boolean} keepExisting (optional) True to keep existing selections
24053      */
24054     selectRow : function(index, keepExisting, preventViewNotify)
24055     {
24056             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24057             return;
24058         }
24059         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24060             if(!keepExisting || this.singleSelect){
24061                 this.clearSelections();
24062             }
24063             
24064             var r = this.grid.store.getAt(index);
24065             //console.log('selectRow - record id :' + r.id);
24066             
24067             this.selections.add(r);
24068             this.last = this.lastActive = index;
24069             if(!preventViewNotify){
24070                 var proxy = new Roo.Element(
24071                                 this.grid.getRowDom(index)
24072                 );
24073                 proxy.addClass('bg-info info');
24074             }
24075             this.fireEvent("rowselect", this, index, r);
24076             this.fireEvent("selectionchange", this);
24077         }
24078     },
24079
24080     /**
24081      * Deselects a row.
24082      * @param {Number} row The index of the row to deselect
24083      */
24084     deselectRow : function(index, preventViewNotify)
24085     {
24086         if(this.locked) {
24087             return;
24088         }
24089         if(this.last == index){
24090             this.last = false;
24091         }
24092         if(this.lastActive == index){
24093             this.lastActive = false;
24094         }
24095         
24096         var r = this.grid.store.getAt(index);
24097         if (!r) {
24098             return;
24099         }
24100         
24101         this.selections.remove(r);
24102         //.console.log('deselectRow - record id :' + r.id);
24103         if(!preventViewNotify){
24104         
24105             var proxy = new Roo.Element(
24106                 this.grid.getRowDom(index)
24107             );
24108             proxy.removeClass('bg-info info');
24109         }
24110         this.fireEvent("rowdeselect", this, index);
24111         this.fireEvent("selectionchange", this);
24112     },
24113
24114     // private
24115     restoreLast : function(){
24116         if(this._last){
24117             this.last = this._last;
24118         }
24119     },
24120
24121     // private
24122     acceptsNav : function(row, col, cm){
24123         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24124     },
24125
24126     // private
24127     onEditorKey : function(field, e){
24128         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24129         if(k == e.TAB){
24130             e.stopEvent();
24131             ed.completeEdit();
24132             if(e.shiftKey){
24133                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24134             }else{
24135                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24136             }
24137         }else if(k == e.ENTER && !e.ctrlKey){
24138             e.stopEvent();
24139             ed.completeEdit();
24140             if(e.shiftKey){
24141                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24142             }else{
24143                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24144             }
24145         }else if(k == e.ESC){
24146             ed.cancelEdit();
24147         }
24148         if(newCell){
24149             g.startEditing(newCell[0], newCell[1]);
24150         }
24151     }
24152 });
24153 /*
24154  * Based on:
24155  * Ext JS Library 1.1.1
24156  * Copyright(c) 2006-2007, Ext JS, LLC.
24157  *
24158  * Originally Released Under LGPL - original licence link has changed is not relivant.
24159  *
24160  * Fork - LGPL
24161  * <script type="text/javascript">
24162  */
24163  
24164 /**
24165  * @class Roo.bootstrap.PagingToolbar
24166  * @extends Roo.bootstrap.NavSimplebar
24167  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24168  * @constructor
24169  * Create a new PagingToolbar
24170  * @param {Object} config The config object
24171  * @param {Roo.data.Store} store
24172  */
24173 Roo.bootstrap.PagingToolbar = function(config)
24174 {
24175     // old args format still supported... - xtype is prefered..
24176         // created from xtype...
24177     
24178     this.ds = config.dataSource;
24179     
24180     if (config.store && !this.ds) {
24181         this.store= Roo.factory(config.store, Roo.data);
24182         this.ds = this.store;
24183         this.ds.xmodule = this.xmodule || false;
24184     }
24185     
24186     this.toolbarItems = [];
24187     if (config.items) {
24188         this.toolbarItems = config.items;
24189     }
24190     
24191     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24192     
24193     this.cursor = 0;
24194     
24195     if (this.ds) { 
24196         this.bind(this.ds);
24197     }
24198     
24199     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24200     
24201 };
24202
24203 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24204     /**
24205      * @cfg {Roo.data.Store} dataSource
24206      * The underlying data store providing the paged data
24207      */
24208     /**
24209      * @cfg {String/HTMLElement/Element} container
24210      * container The id or element that will contain the toolbar
24211      */
24212     /**
24213      * @cfg {Boolean} displayInfo
24214      * True to display the displayMsg (defaults to false)
24215      */
24216     /**
24217      * @cfg {Number} pageSize
24218      * The number of records to display per page (defaults to 20)
24219      */
24220     pageSize: 20,
24221     /**
24222      * @cfg {String} displayMsg
24223      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24224      */
24225     displayMsg : 'Displaying {0} - {1} of {2}',
24226     /**
24227      * @cfg {String} emptyMsg
24228      * The message to display when no records are found (defaults to "No data to display")
24229      */
24230     emptyMsg : 'No data to display',
24231     /**
24232      * Customizable piece of the default paging text (defaults to "Page")
24233      * @type String
24234      */
24235     beforePageText : "Page",
24236     /**
24237      * Customizable piece of the default paging text (defaults to "of %0")
24238      * @type String
24239      */
24240     afterPageText : "of {0}",
24241     /**
24242      * Customizable piece of the default paging text (defaults to "First Page")
24243      * @type String
24244      */
24245     firstText : "First Page",
24246     /**
24247      * Customizable piece of the default paging text (defaults to "Previous Page")
24248      * @type String
24249      */
24250     prevText : "Previous Page",
24251     /**
24252      * Customizable piece of the default paging text (defaults to "Next Page")
24253      * @type String
24254      */
24255     nextText : "Next Page",
24256     /**
24257      * Customizable piece of the default paging text (defaults to "Last Page")
24258      * @type String
24259      */
24260     lastText : "Last Page",
24261     /**
24262      * Customizable piece of the default paging text (defaults to "Refresh")
24263      * @type String
24264      */
24265     refreshText : "Refresh",
24266
24267     buttons : false,
24268     // private
24269     onRender : function(ct, position) 
24270     {
24271         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24272         this.navgroup.parentId = this.id;
24273         this.navgroup.onRender(this.el, null);
24274         // add the buttons to the navgroup
24275         
24276         if(this.displayInfo){
24277             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24278             this.displayEl = this.el.select('.x-paging-info', true).first();
24279 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24280 //            this.displayEl = navel.el.select('span',true).first();
24281         }
24282         
24283         var _this = this;
24284         
24285         if(this.buttons){
24286             Roo.each(_this.buttons, function(e){ // this might need to use render????
24287                Roo.factory(e).onRender(_this.el, null);
24288             });
24289         }
24290             
24291         Roo.each(_this.toolbarItems, function(e) {
24292             _this.navgroup.addItem(e);
24293         });
24294         
24295         
24296         this.first = this.navgroup.addItem({
24297             tooltip: this.firstText,
24298             cls: "prev",
24299             icon : 'fa fa-backward',
24300             disabled: true,
24301             preventDefault: true,
24302             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24303         });
24304         
24305         this.prev =  this.navgroup.addItem({
24306             tooltip: this.prevText,
24307             cls: "prev",
24308             icon : 'fa fa-step-backward',
24309             disabled: true,
24310             preventDefault: true,
24311             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24312         });
24313     //this.addSeparator();
24314         
24315         
24316         var field = this.navgroup.addItem( {
24317             tagtype : 'span',
24318             cls : 'x-paging-position',
24319             
24320             html : this.beforePageText  +
24321                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24322                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24323          } ); //?? escaped?
24324         
24325         this.field = field.el.select('input', true).first();
24326         this.field.on("keydown", this.onPagingKeydown, this);
24327         this.field.on("focus", function(){this.dom.select();});
24328     
24329     
24330         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24331         //this.field.setHeight(18);
24332         //this.addSeparator();
24333         this.next = this.navgroup.addItem({
24334             tooltip: this.nextText,
24335             cls: "next",
24336             html : ' <i class="fa fa-step-forward">',
24337             disabled: true,
24338             preventDefault: true,
24339             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24340         });
24341         this.last = this.navgroup.addItem({
24342             tooltip: this.lastText,
24343             icon : 'fa fa-forward',
24344             cls: "next",
24345             disabled: true,
24346             preventDefault: true,
24347             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24348         });
24349     //this.addSeparator();
24350         this.loading = this.navgroup.addItem({
24351             tooltip: this.refreshText,
24352             icon: 'fa fa-refresh',
24353             preventDefault: true,
24354             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24355         });
24356         
24357     },
24358
24359     // private
24360     updateInfo : function(){
24361         if(this.displayEl){
24362             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24363             var msg = count == 0 ?
24364                 this.emptyMsg :
24365                 String.format(
24366                     this.displayMsg,
24367                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24368                 );
24369             this.displayEl.update(msg);
24370         }
24371     },
24372
24373     // private
24374     onLoad : function(ds, r, o)
24375     {
24376         this.cursor = o.params ? o.params.start : 0;
24377         var d = this.getPageData(),
24378             ap = d.activePage,
24379             ps = d.pages;
24380         
24381         
24382         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24383         this.field.dom.value = ap;
24384         this.first.setDisabled(ap == 1);
24385         this.prev.setDisabled(ap == 1);
24386         this.next.setDisabled(ap == ps);
24387         this.last.setDisabled(ap == ps);
24388         this.loading.enable();
24389         this.updateInfo();
24390     },
24391
24392     // private
24393     getPageData : function(){
24394         var total = this.ds.getTotalCount();
24395         return {
24396             total : total,
24397             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24398             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24399         };
24400     },
24401
24402     // private
24403     onLoadError : function(){
24404         this.loading.enable();
24405     },
24406
24407     // private
24408     onPagingKeydown : function(e){
24409         var k = e.getKey();
24410         var d = this.getPageData();
24411         if(k == e.RETURN){
24412             var v = this.field.dom.value, pageNum;
24413             if(!v || isNaN(pageNum = parseInt(v, 10))){
24414                 this.field.dom.value = d.activePage;
24415                 return;
24416             }
24417             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24418             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24419             e.stopEvent();
24420         }
24421         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))
24422         {
24423           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24424           this.field.dom.value = pageNum;
24425           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24426           e.stopEvent();
24427         }
24428         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24429         {
24430           var v = this.field.dom.value, pageNum; 
24431           var increment = (e.shiftKey) ? 10 : 1;
24432           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24433                 increment *= -1;
24434           }
24435           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24436             this.field.dom.value = d.activePage;
24437             return;
24438           }
24439           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24440           {
24441             this.field.dom.value = parseInt(v, 10) + increment;
24442             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24443             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24444           }
24445           e.stopEvent();
24446         }
24447     },
24448
24449     // private
24450     beforeLoad : function(){
24451         if(this.loading){
24452             this.loading.disable();
24453         }
24454     },
24455
24456     // private
24457     onClick : function(which){
24458         
24459         var ds = this.ds;
24460         if (!ds) {
24461             return;
24462         }
24463         
24464         switch(which){
24465             case "first":
24466                 ds.load({params:{start: 0, limit: this.pageSize}});
24467             break;
24468             case "prev":
24469                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24470             break;
24471             case "next":
24472                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24473             break;
24474             case "last":
24475                 var total = ds.getTotalCount();
24476                 var extra = total % this.pageSize;
24477                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24478                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24479             break;
24480             case "refresh":
24481                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24482             break;
24483         }
24484     },
24485
24486     /**
24487      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24488      * @param {Roo.data.Store} store The data store to unbind
24489      */
24490     unbind : function(ds){
24491         ds.un("beforeload", this.beforeLoad, this);
24492         ds.un("load", this.onLoad, this);
24493         ds.un("loadexception", this.onLoadError, this);
24494         ds.un("remove", this.updateInfo, this);
24495         ds.un("add", this.updateInfo, this);
24496         this.ds = undefined;
24497     },
24498
24499     /**
24500      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24501      * @param {Roo.data.Store} store The data store to bind
24502      */
24503     bind : function(ds){
24504         ds.on("beforeload", this.beforeLoad, this);
24505         ds.on("load", this.onLoad, this);
24506         ds.on("loadexception", this.onLoadError, this);
24507         ds.on("remove", this.updateInfo, this);
24508         ds.on("add", this.updateInfo, this);
24509         this.ds = ds;
24510     }
24511 });/*
24512  * - LGPL
24513  *
24514  * element
24515  * 
24516  */
24517
24518 /**
24519  * @class Roo.bootstrap.MessageBar
24520  * @extends Roo.bootstrap.Component
24521  * Bootstrap MessageBar class
24522  * @cfg {String} html contents of the MessageBar
24523  * @cfg {String} weight (info | success | warning | danger) default info
24524  * @cfg {String} beforeClass insert the bar before the given class
24525  * @cfg {Boolean} closable (true | false) default false
24526  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24527  * 
24528  * @constructor
24529  * Create a new Element
24530  * @param {Object} config The config object
24531  */
24532
24533 Roo.bootstrap.MessageBar = function(config){
24534     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24535 };
24536
24537 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24538     
24539     html: '',
24540     weight: 'info',
24541     closable: false,
24542     fixed: false,
24543     beforeClass: 'bootstrap-sticky-wrap',
24544     
24545     getAutoCreate : function(){
24546         
24547         var cfg = {
24548             tag: 'div',
24549             cls: 'alert alert-dismissable alert-' + this.weight,
24550             cn: [
24551                 {
24552                     tag: 'span',
24553                     cls: 'message',
24554                     html: this.html || ''
24555                 }
24556             ]
24557         };
24558         
24559         if(this.fixed){
24560             cfg.cls += ' alert-messages-fixed';
24561         }
24562         
24563         if(this.closable){
24564             cfg.cn.push({
24565                 tag: 'button',
24566                 cls: 'close',
24567                 html: 'x'
24568             });
24569         }
24570         
24571         return cfg;
24572     },
24573     
24574     onRender : function(ct, position)
24575     {
24576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24577         
24578         if(!this.el){
24579             var cfg = Roo.apply({},  this.getAutoCreate());
24580             cfg.id = Roo.id();
24581             
24582             if (this.cls) {
24583                 cfg.cls += ' ' + this.cls;
24584             }
24585             if (this.style) {
24586                 cfg.style = this.style;
24587             }
24588             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24589             
24590             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24591         }
24592         
24593         this.el.select('>button.close').on('click', this.hide, this);
24594         
24595     },
24596     
24597     show : function()
24598     {
24599         if (!this.rendered) {
24600             this.render();
24601         }
24602         
24603         this.el.show();
24604         
24605         this.fireEvent('show', this);
24606         
24607     },
24608     
24609     hide : function()
24610     {
24611         if (!this.rendered) {
24612             this.render();
24613         }
24614         
24615         this.el.hide();
24616         
24617         this.fireEvent('hide', this);
24618     },
24619     
24620     update : function()
24621     {
24622 //        var e = this.el.dom.firstChild;
24623 //        
24624 //        if(this.closable){
24625 //            e = e.nextSibling;
24626 //        }
24627 //        
24628 //        e.data = this.html || '';
24629
24630         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24631     }
24632    
24633 });
24634
24635  
24636
24637      /*
24638  * - LGPL
24639  *
24640  * Graph
24641  * 
24642  */
24643
24644
24645 /**
24646  * @class Roo.bootstrap.Graph
24647  * @extends Roo.bootstrap.Component
24648  * Bootstrap Graph class
24649 > Prameters
24650  -sm {number} sm 4
24651  -md {number} md 5
24652  @cfg {String} graphtype  bar | vbar | pie
24653  @cfg {number} g_x coodinator | centre x (pie)
24654  @cfg {number} g_y coodinator | centre y (pie)
24655  @cfg {number} g_r radius (pie)
24656  @cfg {number} g_height height of the chart (respected by all elements in the set)
24657  @cfg {number} g_width width of the chart (respected by all elements in the set)
24658  @cfg {Object} title The title of the chart
24659     
24660  -{Array}  values
24661  -opts (object) options for the chart 
24662      o {
24663      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24664      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24665      o vgutter (number)
24666      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.
24667      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24668      o to
24669      o stretch (boolean)
24670      o }
24671  -opts (object) options for the pie
24672      o{
24673      o cut
24674      o startAngle (number)
24675      o endAngle (number)
24676      } 
24677  *
24678  * @constructor
24679  * Create a new Input
24680  * @param {Object} config The config object
24681  */
24682
24683 Roo.bootstrap.Graph = function(config){
24684     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24685     
24686     this.addEvents({
24687         // img events
24688         /**
24689          * @event click
24690          * The img click event for the img.
24691          * @param {Roo.EventObject} e
24692          */
24693         "click" : true
24694     });
24695 };
24696
24697 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24698     
24699     sm: 4,
24700     md: 5,
24701     graphtype: 'bar',
24702     g_height: 250,
24703     g_width: 400,
24704     g_x: 50,
24705     g_y: 50,
24706     g_r: 30,
24707     opts:{
24708         //g_colors: this.colors,
24709         g_type: 'soft',
24710         g_gutter: '20%'
24711
24712     },
24713     title : false,
24714
24715     getAutoCreate : function(){
24716         
24717         var cfg = {
24718             tag: 'div',
24719             html : null
24720         };
24721         
24722         
24723         return  cfg;
24724     },
24725
24726     onRender : function(ct,position){
24727         
24728         
24729         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24730         
24731         if (typeof(Raphael) == 'undefined') {
24732             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24733             return;
24734         }
24735         
24736         this.raphael = Raphael(this.el.dom);
24737         
24738                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24739                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24740                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24741                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24742                 /*
24743                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24744                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24745                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24746                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24747                 
24748                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24749                 r.barchart(330, 10, 300, 220, data1);
24750                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24751                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24752                 */
24753                 
24754                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24755                 // r.barchart(30, 30, 560, 250,  xdata, {
24756                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24757                 //     axis : "0 0 1 1",
24758                 //     axisxlabels :  xdata
24759                 //     //yvalues : cols,
24760                    
24761                 // });
24762 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24763 //        
24764 //        this.load(null,xdata,{
24765 //                axis : "0 0 1 1",
24766 //                axisxlabels :  xdata
24767 //                });
24768
24769     },
24770
24771     load : function(graphtype,xdata,opts)
24772     {
24773         this.raphael.clear();
24774         if(!graphtype) {
24775             graphtype = this.graphtype;
24776         }
24777         if(!opts){
24778             opts = this.opts;
24779         }
24780         var r = this.raphael,
24781             fin = function () {
24782                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24783             },
24784             fout = function () {
24785                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24786             },
24787             pfin = function() {
24788                 this.sector.stop();
24789                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24790
24791                 if (this.label) {
24792                     this.label[0].stop();
24793                     this.label[0].attr({ r: 7.5 });
24794                     this.label[1].attr({ "font-weight": 800 });
24795                 }
24796             },
24797             pfout = function() {
24798                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24799
24800                 if (this.label) {
24801                     this.label[0].animate({ r: 5 }, 500, "bounce");
24802                     this.label[1].attr({ "font-weight": 400 });
24803                 }
24804             };
24805
24806         switch(graphtype){
24807             case 'bar':
24808                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24809                 break;
24810             case 'hbar':
24811                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24812                 break;
24813             case 'pie':
24814 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24815 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24816 //            
24817                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24818                 
24819                 break;
24820
24821         }
24822         
24823         if(this.title){
24824             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24825         }
24826         
24827     },
24828     
24829     setTitle: function(o)
24830     {
24831         this.title = o;
24832     },
24833     
24834     initEvents: function() {
24835         
24836         if(!this.href){
24837             this.el.on('click', this.onClick, this);
24838         }
24839     },
24840     
24841     onClick : function(e)
24842     {
24843         Roo.log('img onclick');
24844         this.fireEvent('click', this, e);
24845     }
24846    
24847 });
24848
24849  
24850 /*
24851  * - LGPL
24852  *
24853  * numberBox
24854  * 
24855  */
24856 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24857
24858 /**
24859  * @class Roo.bootstrap.dash.NumberBox
24860  * @extends Roo.bootstrap.Component
24861  * Bootstrap NumberBox class
24862  * @cfg {String} headline Box headline
24863  * @cfg {String} content Box content
24864  * @cfg {String} icon Box icon
24865  * @cfg {String} footer Footer text
24866  * @cfg {String} fhref Footer href
24867  * 
24868  * @constructor
24869  * Create a new NumberBox
24870  * @param {Object} config The config object
24871  */
24872
24873
24874 Roo.bootstrap.dash.NumberBox = function(config){
24875     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24876     
24877 };
24878
24879 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24880     
24881     headline : '',
24882     content : '',
24883     icon : '',
24884     footer : '',
24885     fhref : '',
24886     ficon : '',
24887     
24888     getAutoCreate : function(){
24889         
24890         var cfg = {
24891             tag : 'div',
24892             cls : 'small-box ',
24893             cn : [
24894                 {
24895                     tag : 'div',
24896                     cls : 'inner',
24897                     cn :[
24898                         {
24899                             tag : 'h3',
24900                             cls : 'roo-headline',
24901                             html : this.headline
24902                         },
24903                         {
24904                             tag : 'p',
24905                             cls : 'roo-content',
24906                             html : this.content
24907                         }
24908                     ]
24909                 }
24910             ]
24911         };
24912         
24913         if(this.icon){
24914             cfg.cn.push({
24915                 tag : 'div',
24916                 cls : 'icon',
24917                 cn :[
24918                     {
24919                         tag : 'i',
24920                         cls : 'ion ' + this.icon
24921                     }
24922                 ]
24923             });
24924         }
24925         
24926         if(this.footer){
24927             var footer = {
24928                 tag : 'a',
24929                 cls : 'small-box-footer',
24930                 href : this.fhref || '#',
24931                 html : this.footer
24932             };
24933             
24934             cfg.cn.push(footer);
24935             
24936         }
24937         
24938         return  cfg;
24939     },
24940
24941     onRender : function(ct,position){
24942         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24943
24944
24945        
24946                 
24947     },
24948
24949     setHeadline: function (value)
24950     {
24951         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24952     },
24953     
24954     setFooter: function (value, href)
24955     {
24956         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24957         
24958         if(href){
24959             this.el.select('a.small-box-footer',true).first().attr('href', href);
24960         }
24961         
24962     },
24963
24964     setContent: function (value)
24965     {
24966         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24967     },
24968
24969     initEvents: function() 
24970     {   
24971         
24972     }
24973     
24974 });
24975
24976  
24977 /*
24978  * - LGPL
24979  *
24980  * TabBox
24981  * 
24982  */
24983 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24984
24985 /**
24986  * @class Roo.bootstrap.dash.TabBox
24987  * @extends Roo.bootstrap.Component
24988  * Bootstrap TabBox class
24989  * @cfg {String} title Title of the TabBox
24990  * @cfg {String} icon Icon of the TabBox
24991  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24992  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24993  * 
24994  * @constructor
24995  * Create a new TabBox
24996  * @param {Object} config The config object
24997  */
24998
24999
25000 Roo.bootstrap.dash.TabBox = function(config){
25001     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25002     this.addEvents({
25003         // raw events
25004         /**
25005          * @event addpane
25006          * When a pane is added
25007          * @param {Roo.bootstrap.dash.TabPane} pane
25008          */
25009         "addpane" : true,
25010         /**
25011          * @event activatepane
25012          * When a pane is activated
25013          * @param {Roo.bootstrap.dash.TabPane} pane
25014          */
25015         "activatepane" : true
25016         
25017          
25018     });
25019     
25020     this.panes = [];
25021 };
25022
25023 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25024
25025     title : '',
25026     icon : false,
25027     showtabs : true,
25028     tabScrollable : false,
25029     
25030     getChildContainer : function()
25031     {
25032         return this.el.select('.tab-content', true).first();
25033     },
25034     
25035     getAutoCreate : function(){
25036         
25037         var header = {
25038             tag: 'li',
25039             cls: 'pull-left header',
25040             html: this.title,
25041             cn : []
25042         };
25043         
25044         if(this.icon){
25045             header.cn.push({
25046                 tag: 'i',
25047                 cls: 'fa ' + this.icon
25048             });
25049         }
25050         
25051         var h = {
25052             tag: 'ul',
25053             cls: 'nav nav-tabs pull-right',
25054             cn: [
25055                 header
25056             ]
25057         };
25058         
25059         if(this.tabScrollable){
25060             h = {
25061                 tag: 'div',
25062                 cls: 'tab-header',
25063                 cn: [
25064                     {
25065                         tag: 'ul',
25066                         cls: 'nav nav-tabs pull-right',
25067                         cn: [
25068                             header
25069                         ]
25070                     }
25071                 ]
25072             };
25073         }
25074         
25075         var cfg = {
25076             tag: 'div',
25077             cls: 'nav-tabs-custom',
25078             cn: [
25079                 h,
25080                 {
25081                     tag: 'div',
25082                     cls: 'tab-content no-padding',
25083                     cn: []
25084                 }
25085             ]
25086         };
25087
25088         return  cfg;
25089     },
25090     initEvents : function()
25091     {
25092         //Roo.log('add add pane handler');
25093         this.on('addpane', this.onAddPane, this);
25094     },
25095      /**
25096      * Updates the box title
25097      * @param {String} html to set the title to.
25098      */
25099     setTitle : function(value)
25100     {
25101         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25102     },
25103     onAddPane : function(pane)
25104     {
25105         this.panes.push(pane);
25106         //Roo.log('addpane');
25107         //Roo.log(pane);
25108         // tabs are rendere left to right..
25109         if(!this.showtabs){
25110             return;
25111         }
25112         
25113         var ctr = this.el.select('.nav-tabs', true).first();
25114          
25115          
25116         var existing = ctr.select('.nav-tab',true);
25117         var qty = existing.getCount();;
25118         
25119         
25120         var tab = ctr.createChild({
25121             tag : 'li',
25122             cls : 'nav-tab' + (qty ? '' : ' active'),
25123             cn : [
25124                 {
25125                     tag : 'a',
25126                     href:'#',
25127                     html : pane.title
25128                 }
25129             ]
25130         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25131         pane.tab = tab;
25132         
25133         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25134         if (!qty) {
25135             pane.el.addClass('active');
25136         }
25137         
25138                 
25139     },
25140     onTabClick : function(ev,un,ob,pane)
25141     {
25142         //Roo.log('tab - prev default');
25143         ev.preventDefault();
25144         
25145         
25146         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25147         pane.tab.addClass('active');
25148         //Roo.log(pane.title);
25149         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25150         // technically we should have a deactivate event.. but maybe add later.
25151         // and it should not de-activate the selected tab...
25152         this.fireEvent('activatepane', pane);
25153         pane.el.addClass('active');
25154         pane.fireEvent('activate');
25155         
25156         
25157     },
25158     
25159     getActivePane : function()
25160     {
25161         var r = false;
25162         Roo.each(this.panes, function(p) {
25163             if(p.el.hasClass('active')){
25164                 r = p;
25165                 return false;
25166             }
25167             
25168             return;
25169         });
25170         
25171         return r;
25172     }
25173     
25174     
25175 });
25176
25177  
25178 /*
25179  * - LGPL
25180  *
25181  * Tab pane
25182  * 
25183  */
25184 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25185 /**
25186  * @class Roo.bootstrap.TabPane
25187  * @extends Roo.bootstrap.Component
25188  * Bootstrap TabPane class
25189  * @cfg {Boolean} active (false | true) Default false
25190  * @cfg {String} title title of panel
25191
25192  * 
25193  * @constructor
25194  * Create a new TabPane
25195  * @param {Object} config The config object
25196  */
25197
25198 Roo.bootstrap.dash.TabPane = function(config){
25199     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25200     
25201     this.addEvents({
25202         // raw events
25203         /**
25204          * @event activate
25205          * When a pane is activated
25206          * @param {Roo.bootstrap.dash.TabPane} pane
25207          */
25208         "activate" : true
25209          
25210     });
25211 };
25212
25213 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25214     
25215     active : false,
25216     title : '',
25217     
25218     // the tabBox that this is attached to.
25219     tab : false,
25220      
25221     getAutoCreate : function() 
25222     {
25223         var cfg = {
25224             tag: 'div',
25225             cls: 'tab-pane'
25226         };
25227         
25228         if(this.active){
25229             cfg.cls += ' active';
25230         }
25231         
25232         return cfg;
25233     },
25234     initEvents  : function()
25235     {
25236         //Roo.log('trigger add pane handler');
25237         this.parent().fireEvent('addpane', this)
25238     },
25239     
25240      /**
25241      * Updates the tab title 
25242      * @param {String} html to set the title to.
25243      */
25244     setTitle: function(str)
25245     {
25246         if (!this.tab) {
25247             return;
25248         }
25249         this.title = str;
25250         this.tab.select('a', true).first().dom.innerHTML = str;
25251         
25252     }
25253     
25254     
25255     
25256 });
25257
25258  
25259
25260
25261  /*
25262  * - LGPL
25263  *
25264  * menu
25265  * 
25266  */
25267 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25268
25269 /**
25270  * @class Roo.bootstrap.menu.Menu
25271  * @extends Roo.bootstrap.Component
25272  * Bootstrap Menu class - container for Menu
25273  * @cfg {String} html Text of the menu
25274  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25275  * @cfg {String} icon Font awesome icon
25276  * @cfg {String} pos Menu align to (top | bottom) default bottom
25277  * 
25278  * 
25279  * @constructor
25280  * Create a new Menu
25281  * @param {Object} config The config object
25282  */
25283
25284
25285 Roo.bootstrap.menu.Menu = function(config){
25286     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25287     
25288     this.addEvents({
25289         /**
25290          * @event beforeshow
25291          * Fires before this menu is displayed
25292          * @param {Roo.bootstrap.menu.Menu} this
25293          */
25294         beforeshow : true,
25295         /**
25296          * @event beforehide
25297          * Fires before this menu is hidden
25298          * @param {Roo.bootstrap.menu.Menu} this
25299          */
25300         beforehide : true,
25301         /**
25302          * @event show
25303          * Fires after this menu is displayed
25304          * @param {Roo.bootstrap.menu.Menu} this
25305          */
25306         show : true,
25307         /**
25308          * @event hide
25309          * Fires after this menu is hidden
25310          * @param {Roo.bootstrap.menu.Menu} this
25311          */
25312         hide : true,
25313         /**
25314          * @event click
25315          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25316          * @param {Roo.bootstrap.menu.Menu} this
25317          * @param {Roo.EventObject} e
25318          */
25319         click : true
25320     });
25321     
25322 };
25323
25324 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25325     
25326     submenu : false,
25327     html : '',
25328     weight : 'default',
25329     icon : false,
25330     pos : 'bottom',
25331     
25332     
25333     getChildContainer : function() {
25334         if(this.isSubMenu){
25335             return this.el;
25336         }
25337         
25338         return this.el.select('ul.dropdown-menu', true).first();  
25339     },
25340     
25341     getAutoCreate : function()
25342     {
25343         var text = [
25344             {
25345                 tag : 'span',
25346                 cls : 'roo-menu-text',
25347                 html : this.html
25348             }
25349         ];
25350         
25351         if(this.icon){
25352             text.unshift({
25353                 tag : 'i',
25354                 cls : 'fa ' + this.icon
25355             })
25356         }
25357         
25358         
25359         var cfg = {
25360             tag : 'div',
25361             cls : 'btn-group',
25362             cn : [
25363                 {
25364                     tag : 'button',
25365                     cls : 'dropdown-button btn btn-' + this.weight,
25366                     cn : text
25367                 },
25368                 {
25369                     tag : 'button',
25370                     cls : 'dropdown-toggle btn btn-' + this.weight,
25371                     cn : [
25372                         {
25373                             tag : 'span',
25374                             cls : 'caret'
25375                         }
25376                     ]
25377                 },
25378                 {
25379                     tag : 'ul',
25380                     cls : 'dropdown-menu'
25381                 }
25382             ]
25383             
25384         };
25385         
25386         if(this.pos == 'top'){
25387             cfg.cls += ' dropup';
25388         }
25389         
25390         if(this.isSubMenu){
25391             cfg = {
25392                 tag : 'ul',
25393                 cls : 'dropdown-menu'
25394             }
25395         }
25396         
25397         return cfg;
25398     },
25399     
25400     onRender : function(ct, position)
25401     {
25402         this.isSubMenu = ct.hasClass('dropdown-submenu');
25403         
25404         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25405     },
25406     
25407     initEvents : function() 
25408     {
25409         if(this.isSubMenu){
25410             return;
25411         }
25412         
25413         this.hidden = true;
25414         
25415         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25416         this.triggerEl.on('click', this.onTriggerPress, this);
25417         
25418         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25419         this.buttonEl.on('click', this.onClick, this);
25420         
25421     },
25422     
25423     list : function()
25424     {
25425         if(this.isSubMenu){
25426             return this.el;
25427         }
25428         
25429         return this.el.select('ul.dropdown-menu', true).first();
25430     },
25431     
25432     onClick : function(e)
25433     {
25434         this.fireEvent("click", this, e);
25435     },
25436     
25437     onTriggerPress  : function(e)
25438     {   
25439         if (this.isVisible()) {
25440             this.hide();
25441         } else {
25442             this.show();
25443         }
25444     },
25445     
25446     isVisible : function(){
25447         return !this.hidden;
25448     },
25449     
25450     show : function()
25451     {
25452         this.fireEvent("beforeshow", this);
25453         
25454         this.hidden = false;
25455         this.el.addClass('open');
25456         
25457         Roo.get(document).on("mouseup", this.onMouseUp, this);
25458         
25459         this.fireEvent("show", this);
25460         
25461         
25462     },
25463     
25464     hide : function()
25465     {
25466         this.fireEvent("beforehide", this);
25467         
25468         this.hidden = true;
25469         this.el.removeClass('open');
25470         
25471         Roo.get(document).un("mouseup", this.onMouseUp);
25472         
25473         this.fireEvent("hide", this);
25474     },
25475     
25476     onMouseUp : function()
25477     {
25478         this.hide();
25479     }
25480     
25481 });
25482
25483  
25484  /*
25485  * - LGPL
25486  *
25487  * menu item
25488  * 
25489  */
25490 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25491
25492 /**
25493  * @class Roo.bootstrap.menu.Item
25494  * @extends Roo.bootstrap.Component
25495  * Bootstrap MenuItem class
25496  * @cfg {Boolean} submenu (true | false) default false
25497  * @cfg {String} html text of the item
25498  * @cfg {String} href the link
25499  * @cfg {Boolean} disable (true | false) default false
25500  * @cfg {Boolean} preventDefault (true | false) default true
25501  * @cfg {String} icon Font awesome icon
25502  * @cfg {String} pos Submenu align to (left | right) default right 
25503  * 
25504  * 
25505  * @constructor
25506  * Create a new Item
25507  * @param {Object} config The config object
25508  */
25509
25510
25511 Roo.bootstrap.menu.Item = function(config){
25512     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25513     this.addEvents({
25514         /**
25515          * @event mouseover
25516          * Fires when the mouse is hovering over this menu
25517          * @param {Roo.bootstrap.menu.Item} this
25518          * @param {Roo.EventObject} e
25519          */
25520         mouseover : true,
25521         /**
25522          * @event mouseout
25523          * Fires when the mouse exits this menu
25524          * @param {Roo.bootstrap.menu.Item} this
25525          * @param {Roo.EventObject} e
25526          */
25527         mouseout : true,
25528         // raw events
25529         /**
25530          * @event click
25531          * The raw click event for the entire grid.
25532          * @param {Roo.EventObject} e
25533          */
25534         click : true
25535     });
25536 };
25537
25538 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25539     
25540     submenu : false,
25541     href : '',
25542     html : '',
25543     preventDefault: true,
25544     disable : false,
25545     icon : false,
25546     pos : 'right',
25547     
25548     getAutoCreate : function()
25549     {
25550         var text = [
25551             {
25552                 tag : 'span',
25553                 cls : 'roo-menu-item-text',
25554                 html : this.html
25555             }
25556         ];
25557         
25558         if(this.icon){
25559             text.unshift({
25560                 tag : 'i',
25561                 cls : 'fa ' + this.icon
25562             })
25563         }
25564         
25565         var cfg = {
25566             tag : 'li',
25567             cn : [
25568                 {
25569                     tag : 'a',
25570                     href : this.href || '#',
25571                     cn : text
25572                 }
25573             ]
25574         };
25575         
25576         if(this.disable){
25577             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25578         }
25579         
25580         if(this.submenu){
25581             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25582             
25583             if(this.pos == 'left'){
25584                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25585             }
25586         }
25587         
25588         return cfg;
25589     },
25590     
25591     initEvents : function() 
25592     {
25593         this.el.on('mouseover', this.onMouseOver, this);
25594         this.el.on('mouseout', this.onMouseOut, this);
25595         
25596         this.el.select('a', true).first().on('click', this.onClick, this);
25597         
25598     },
25599     
25600     onClick : function(e)
25601     {
25602         if(this.preventDefault){
25603             e.preventDefault();
25604         }
25605         
25606         this.fireEvent("click", this, e);
25607     },
25608     
25609     onMouseOver : function(e)
25610     {
25611         if(this.submenu && this.pos == 'left'){
25612             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25613         }
25614         
25615         this.fireEvent("mouseover", this, e);
25616     },
25617     
25618     onMouseOut : function(e)
25619     {
25620         this.fireEvent("mouseout", this, e);
25621     }
25622 });
25623
25624  
25625
25626  /*
25627  * - LGPL
25628  *
25629  * menu separator
25630  * 
25631  */
25632 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25633
25634 /**
25635  * @class Roo.bootstrap.menu.Separator
25636  * @extends Roo.bootstrap.Component
25637  * Bootstrap Separator class
25638  * 
25639  * @constructor
25640  * Create a new Separator
25641  * @param {Object} config The config object
25642  */
25643
25644
25645 Roo.bootstrap.menu.Separator = function(config){
25646     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25647 };
25648
25649 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25650     
25651     getAutoCreate : function(){
25652         var cfg = {
25653             tag : 'li',
25654             cls: 'divider'
25655         };
25656         
25657         return cfg;
25658     }
25659    
25660 });
25661
25662  
25663
25664  /*
25665  * - LGPL
25666  *
25667  * Tooltip
25668  * 
25669  */
25670
25671 /**
25672  * @class Roo.bootstrap.Tooltip
25673  * Bootstrap Tooltip class
25674  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25675  * to determine which dom element triggers the tooltip.
25676  * 
25677  * It needs to add support for additional attributes like tooltip-position
25678  * 
25679  * @constructor
25680  * Create a new Toolti
25681  * @param {Object} config The config object
25682  */
25683
25684 Roo.bootstrap.Tooltip = function(config){
25685     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25686     
25687     this.alignment = Roo.bootstrap.Tooltip.alignment;
25688     
25689     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25690         this.alignment = config.alignment;
25691     }
25692     
25693 };
25694
25695 Roo.apply(Roo.bootstrap.Tooltip, {
25696     /**
25697      * @function init initialize tooltip monitoring.
25698      * @static
25699      */
25700     currentEl : false,
25701     currentTip : false,
25702     currentRegion : false,
25703     
25704     //  init : delay?
25705     
25706     init : function()
25707     {
25708         Roo.get(document).on('mouseover', this.enter ,this);
25709         Roo.get(document).on('mouseout', this.leave, this);
25710          
25711         
25712         this.currentTip = new Roo.bootstrap.Tooltip();
25713     },
25714     
25715     enter : function(ev)
25716     {
25717         var dom = ev.getTarget();
25718         
25719         //Roo.log(['enter',dom]);
25720         var el = Roo.fly(dom);
25721         if (this.currentEl) {
25722             //Roo.log(dom);
25723             //Roo.log(this.currentEl);
25724             //Roo.log(this.currentEl.contains(dom));
25725             if (this.currentEl == el) {
25726                 return;
25727             }
25728             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25729                 return;
25730             }
25731
25732         }
25733         
25734         if (this.currentTip.el) {
25735             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25736         }    
25737         //Roo.log(ev);
25738         
25739         if(!el || el.dom == document){
25740             return;
25741         }
25742         
25743         var bindEl = el;
25744         
25745         // you can not look for children, as if el is the body.. then everythign is the child..
25746         if (!el.attr('tooltip')) { //
25747             if (!el.select("[tooltip]").elements.length) {
25748                 return;
25749             }
25750             // is the mouse over this child...?
25751             bindEl = el.select("[tooltip]").first();
25752             var xy = ev.getXY();
25753             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25754                 //Roo.log("not in region.");
25755                 return;
25756             }
25757             //Roo.log("child element over..");
25758             
25759         }
25760         this.currentEl = bindEl;
25761         this.currentTip.bind(bindEl);
25762         this.currentRegion = Roo.lib.Region.getRegion(dom);
25763         this.currentTip.enter();
25764         
25765     },
25766     leave : function(ev)
25767     {
25768         var dom = ev.getTarget();
25769         //Roo.log(['leave',dom]);
25770         if (!this.currentEl) {
25771             return;
25772         }
25773         
25774         
25775         if (dom != this.currentEl.dom) {
25776             return;
25777         }
25778         var xy = ev.getXY();
25779         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25780             return;
25781         }
25782         // only activate leave if mouse cursor is outside... bounding box..
25783         
25784         
25785         
25786         
25787         if (this.currentTip) {
25788             this.currentTip.leave();
25789         }
25790         //Roo.log('clear currentEl');
25791         this.currentEl = false;
25792         
25793         
25794     },
25795     alignment : {
25796         'left' : ['r-l', [-2,0], 'right'],
25797         'right' : ['l-r', [2,0], 'left'],
25798         'bottom' : ['t-b', [0,2], 'top'],
25799         'top' : [ 'b-t', [0,-2], 'bottom']
25800     }
25801     
25802 });
25803
25804
25805 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25806     
25807     
25808     bindEl : false,
25809     
25810     delay : null, // can be { show : 300 , hide: 500}
25811     
25812     timeout : null,
25813     
25814     hoverState : null, //???
25815     
25816     placement : 'bottom', 
25817     
25818     alignment : false,
25819     
25820     getAutoCreate : function(){
25821     
25822         var cfg = {
25823            cls : 'tooltip',
25824            role : 'tooltip',
25825            cn : [
25826                 {
25827                     cls : 'tooltip-arrow'
25828                 },
25829                 {
25830                     cls : 'tooltip-inner'
25831                 }
25832            ]
25833         };
25834         
25835         return cfg;
25836     },
25837     bind : function(el)
25838     {
25839         this.bindEl = el;
25840     },
25841       
25842     
25843     enter : function () {
25844        
25845         if (this.timeout != null) {
25846             clearTimeout(this.timeout);
25847         }
25848         
25849         this.hoverState = 'in';
25850          //Roo.log("enter - show");
25851         if (!this.delay || !this.delay.show) {
25852             this.show();
25853             return;
25854         }
25855         var _t = this;
25856         this.timeout = setTimeout(function () {
25857             if (_t.hoverState == 'in') {
25858                 _t.show();
25859             }
25860         }, this.delay.show);
25861     },
25862     leave : function()
25863     {
25864         clearTimeout(this.timeout);
25865     
25866         this.hoverState = 'out';
25867          if (!this.delay || !this.delay.hide) {
25868             this.hide();
25869             return;
25870         }
25871        
25872         var _t = this;
25873         this.timeout = setTimeout(function () {
25874             //Roo.log("leave - timeout");
25875             
25876             if (_t.hoverState == 'out') {
25877                 _t.hide();
25878                 Roo.bootstrap.Tooltip.currentEl = false;
25879             }
25880         }, delay);
25881     },
25882     
25883     show : function (msg)
25884     {
25885         if (!this.el) {
25886             this.render(document.body);
25887         }
25888         // set content.
25889         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25890         
25891         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25892         
25893         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25894         
25895         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25896         
25897         var placement = typeof this.placement == 'function' ?
25898             this.placement.call(this, this.el, on_el) :
25899             this.placement;
25900             
25901         var autoToken = /\s?auto?\s?/i;
25902         var autoPlace = autoToken.test(placement);
25903         if (autoPlace) {
25904             placement = placement.replace(autoToken, '') || 'top';
25905         }
25906         
25907         //this.el.detach()
25908         //this.el.setXY([0,0]);
25909         this.el.show();
25910         //this.el.dom.style.display='block';
25911         
25912         //this.el.appendTo(on_el);
25913         
25914         var p = this.getPosition();
25915         var box = this.el.getBox();
25916         
25917         if (autoPlace) {
25918             // fixme..
25919         }
25920         
25921         var align = this.alignment[placement];
25922         
25923         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25924         
25925         if(placement == 'top' || placement == 'bottom'){
25926             if(xy[0] < 0){
25927                 placement = 'right';
25928             }
25929             
25930             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25931                 placement = 'left';
25932             }
25933             
25934             var scroll = Roo.select('body', true).first().getScroll();
25935             
25936             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25937                 placement = 'top';
25938             }
25939             
25940         }
25941         
25942         this.el.alignTo(this.bindEl, align[0],align[1]);
25943         //var arrow = this.el.select('.arrow',true).first();
25944         //arrow.set(align[2], 
25945         
25946         this.el.addClass(placement);
25947         
25948         this.el.addClass('in fade');
25949         
25950         this.hoverState = null;
25951         
25952         if (this.el.hasClass('fade')) {
25953             // fade it?
25954         }
25955         
25956     },
25957     hide : function()
25958     {
25959          
25960         if (!this.el) {
25961             return;
25962         }
25963         //this.el.setXY([0,0]);
25964         this.el.removeClass('in');
25965         //this.el.hide();
25966         
25967     }
25968     
25969 });
25970  
25971
25972  /*
25973  * - LGPL
25974  *
25975  * Location Picker
25976  * 
25977  */
25978
25979 /**
25980  * @class Roo.bootstrap.LocationPicker
25981  * @extends Roo.bootstrap.Component
25982  * Bootstrap LocationPicker class
25983  * @cfg {Number} latitude Position when init default 0
25984  * @cfg {Number} longitude Position when init default 0
25985  * @cfg {Number} zoom default 15
25986  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25987  * @cfg {Boolean} mapTypeControl default false
25988  * @cfg {Boolean} disableDoubleClickZoom default false
25989  * @cfg {Boolean} scrollwheel default true
25990  * @cfg {Boolean} streetViewControl default false
25991  * @cfg {Number} radius default 0
25992  * @cfg {String} locationName
25993  * @cfg {Boolean} draggable default true
25994  * @cfg {Boolean} enableAutocomplete default false
25995  * @cfg {Boolean} enableReverseGeocode default true
25996  * @cfg {String} markerTitle
25997  * 
25998  * @constructor
25999  * Create a new LocationPicker
26000  * @param {Object} config The config object
26001  */
26002
26003
26004 Roo.bootstrap.LocationPicker = function(config){
26005     
26006     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26007     
26008     this.addEvents({
26009         /**
26010          * @event initial
26011          * Fires when the picker initialized.
26012          * @param {Roo.bootstrap.LocationPicker} this
26013          * @param {Google Location} location
26014          */
26015         initial : true,
26016         /**
26017          * @event positionchanged
26018          * Fires when the picker position changed.
26019          * @param {Roo.bootstrap.LocationPicker} this
26020          * @param {Google Location} location
26021          */
26022         positionchanged : true,
26023         /**
26024          * @event resize
26025          * Fires when the map resize.
26026          * @param {Roo.bootstrap.LocationPicker} this
26027          */
26028         resize : true,
26029         /**
26030          * @event show
26031          * Fires when the map show.
26032          * @param {Roo.bootstrap.LocationPicker} this
26033          */
26034         show : true,
26035         /**
26036          * @event hide
26037          * Fires when the map hide.
26038          * @param {Roo.bootstrap.LocationPicker} this
26039          */
26040         hide : true,
26041         /**
26042          * @event mapClick
26043          * Fires when click the map.
26044          * @param {Roo.bootstrap.LocationPicker} this
26045          * @param {Map event} e
26046          */
26047         mapClick : true,
26048         /**
26049          * @event mapRightClick
26050          * Fires when right click the map.
26051          * @param {Roo.bootstrap.LocationPicker} this
26052          * @param {Map event} e
26053          */
26054         mapRightClick : true,
26055         /**
26056          * @event markerClick
26057          * Fires when click the marker.
26058          * @param {Roo.bootstrap.LocationPicker} this
26059          * @param {Map event} e
26060          */
26061         markerClick : true,
26062         /**
26063          * @event markerRightClick
26064          * Fires when right click the marker.
26065          * @param {Roo.bootstrap.LocationPicker} this
26066          * @param {Map event} e
26067          */
26068         markerRightClick : true,
26069         /**
26070          * @event OverlayViewDraw
26071          * Fires when OverlayView Draw
26072          * @param {Roo.bootstrap.LocationPicker} this
26073          */
26074         OverlayViewDraw : true,
26075         /**
26076          * @event OverlayViewOnAdd
26077          * Fires when OverlayView Draw
26078          * @param {Roo.bootstrap.LocationPicker} this
26079          */
26080         OverlayViewOnAdd : true,
26081         /**
26082          * @event OverlayViewOnRemove
26083          * Fires when OverlayView Draw
26084          * @param {Roo.bootstrap.LocationPicker} this
26085          */
26086         OverlayViewOnRemove : true,
26087         /**
26088          * @event OverlayViewShow
26089          * Fires when OverlayView Draw
26090          * @param {Roo.bootstrap.LocationPicker} this
26091          * @param {Pixel} cpx
26092          */
26093         OverlayViewShow : true,
26094         /**
26095          * @event OverlayViewHide
26096          * Fires when OverlayView Draw
26097          * @param {Roo.bootstrap.LocationPicker} this
26098          */
26099         OverlayViewHide : true,
26100         /**
26101          * @event loadexception
26102          * Fires when load google lib failed.
26103          * @param {Roo.bootstrap.LocationPicker} this
26104          */
26105         loadexception : true
26106     });
26107         
26108 };
26109
26110 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26111     
26112     gMapContext: false,
26113     
26114     latitude: 0,
26115     longitude: 0,
26116     zoom: 15,
26117     mapTypeId: false,
26118     mapTypeControl: false,
26119     disableDoubleClickZoom: false,
26120     scrollwheel: true,
26121     streetViewControl: false,
26122     radius: 0,
26123     locationName: '',
26124     draggable: true,
26125     enableAutocomplete: false,
26126     enableReverseGeocode: true,
26127     markerTitle: '',
26128     
26129     getAutoCreate: function()
26130     {
26131
26132         var cfg = {
26133             tag: 'div',
26134             cls: 'roo-location-picker'
26135         };
26136         
26137         return cfg
26138     },
26139     
26140     initEvents: function(ct, position)
26141     {       
26142         if(!this.el.getWidth() || this.isApplied()){
26143             return;
26144         }
26145         
26146         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26147         
26148         this.initial();
26149     },
26150     
26151     initial: function()
26152     {
26153         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26154             this.fireEvent('loadexception', this);
26155             return;
26156         }
26157         
26158         if(!this.mapTypeId){
26159             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26160         }
26161         
26162         this.gMapContext = this.GMapContext();
26163         
26164         this.initOverlayView();
26165         
26166         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26167         
26168         var _this = this;
26169                 
26170         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26171             _this.setPosition(_this.gMapContext.marker.position);
26172         });
26173         
26174         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26175             _this.fireEvent('mapClick', this, event);
26176             
26177         });
26178
26179         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26180             _this.fireEvent('mapRightClick', this, event);
26181             
26182         });
26183         
26184         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26185             _this.fireEvent('markerClick', this, event);
26186             
26187         });
26188
26189         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26190             _this.fireEvent('markerRightClick', this, event);
26191             
26192         });
26193         
26194         this.setPosition(this.gMapContext.location);
26195         
26196         this.fireEvent('initial', this, this.gMapContext.location);
26197     },
26198     
26199     initOverlayView: function()
26200     {
26201         var _this = this;
26202         
26203         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26204             
26205             draw: function()
26206             {
26207                 _this.fireEvent('OverlayViewDraw', _this);
26208             },
26209             
26210             onAdd: function()
26211             {
26212                 _this.fireEvent('OverlayViewOnAdd', _this);
26213             },
26214             
26215             onRemove: function()
26216             {
26217                 _this.fireEvent('OverlayViewOnRemove', _this);
26218             },
26219             
26220             show: function(cpx)
26221             {
26222                 _this.fireEvent('OverlayViewShow', _this, cpx);
26223             },
26224             
26225             hide: function()
26226             {
26227                 _this.fireEvent('OverlayViewHide', _this);
26228             }
26229             
26230         });
26231     },
26232     
26233     fromLatLngToContainerPixel: function(event)
26234     {
26235         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26236     },
26237     
26238     isApplied: function() 
26239     {
26240         return this.getGmapContext() == false ? false : true;
26241     },
26242     
26243     getGmapContext: function() 
26244     {
26245         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26246     },
26247     
26248     GMapContext: function() 
26249     {
26250         var position = new google.maps.LatLng(this.latitude, this.longitude);
26251         
26252         var _map = new google.maps.Map(this.el.dom, {
26253             center: position,
26254             zoom: this.zoom,
26255             mapTypeId: this.mapTypeId,
26256             mapTypeControl: this.mapTypeControl,
26257             disableDoubleClickZoom: this.disableDoubleClickZoom,
26258             scrollwheel: this.scrollwheel,
26259             streetViewControl: this.streetViewControl,
26260             locationName: this.locationName,
26261             draggable: this.draggable,
26262             enableAutocomplete: this.enableAutocomplete,
26263             enableReverseGeocode: this.enableReverseGeocode
26264         });
26265         
26266         var _marker = new google.maps.Marker({
26267             position: position,
26268             map: _map,
26269             title: this.markerTitle,
26270             draggable: this.draggable
26271         });
26272         
26273         return {
26274             map: _map,
26275             marker: _marker,
26276             circle: null,
26277             location: position,
26278             radius: this.radius,
26279             locationName: this.locationName,
26280             addressComponents: {
26281                 formatted_address: null,
26282                 addressLine1: null,
26283                 addressLine2: null,
26284                 streetName: null,
26285                 streetNumber: null,
26286                 city: null,
26287                 district: null,
26288                 state: null,
26289                 stateOrProvince: null
26290             },
26291             settings: this,
26292             domContainer: this.el.dom,
26293             geodecoder: new google.maps.Geocoder()
26294         };
26295     },
26296     
26297     drawCircle: function(center, radius, options) 
26298     {
26299         if (this.gMapContext.circle != null) {
26300             this.gMapContext.circle.setMap(null);
26301         }
26302         if (radius > 0) {
26303             radius *= 1;
26304             options = Roo.apply({}, options, {
26305                 strokeColor: "#0000FF",
26306                 strokeOpacity: .35,
26307                 strokeWeight: 2,
26308                 fillColor: "#0000FF",
26309                 fillOpacity: .2
26310             });
26311             
26312             options.map = this.gMapContext.map;
26313             options.radius = radius;
26314             options.center = center;
26315             this.gMapContext.circle = new google.maps.Circle(options);
26316             return this.gMapContext.circle;
26317         }
26318         
26319         return null;
26320     },
26321     
26322     setPosition: function(location) 
26323     {
26324         this.gMapContext.location = location;
26325         this.gMapContext.marker.setPosition(location);
26326         this.gMapContext.map.panTo(location);
26327         this.drawCircle(location, this.gMapContext.radius, {});
26328         
26329         var _this = this;
26330         
26331         if (this.gMapContext.settings.enableReverseGeocode) {
26332             this.gMapContext.geodecoder.geocode({
26333                 latLng: this.gMapContext.location
26334             }, function(results, status) {
26335                 
26336                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26337                     _this.gMapContext.locationName = results[0].formatted_address;
26338                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26339                     
26340                     _this.fireEvent('positionchanged', this, location);
26341                 }
26342             });
26343             
26344             return;
26345         }
26346         
26347         this.fireEvent('positionchanged', this, location);
26348     },
26349     
26350     resize: function()
26351     {
26352         google.maps.event.trigger(this.gMapContext.map, "resize");
26353         
26354         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26355         
26356         this.fireEvent('resize', this);
26357     },
26358     
26359     setPositionByLatLng: function(latitude, longitude)
26360     {
26361         this.setPosition(new google.maps.LatLng(latitude, longitude));
26362     },
26363     
26364     getCurrentPosition: function() 
26365     {
26366         return {
26367             latitude: this.gMapContext.location.lat(),
26368             longitude: this.gMapContext.location.lng()
26369         };
26370     },
26371     
26372     getAddressName: function() 
26373     {
26374         return this.gMapContext.locationName;
26375     },
26376     
26377     getAddressComponents: function() 
26378     {
26379         return this.gMapContext.addressComponents;
26380     },
26381     
26382     address_component_from_google_geocode: function(address_components) 
26383     {
26384         var result = {};
26385         
26386         for (var i = 0; i < address_components.length; i++) {
26387             var component = address_components[i];
26388             if (component.types.indexOf("postal_code") >= 0) {
26389                 result.postalCode = component.short_name;
26390             } else if (component.types.indexOf("street_number") >= 0) {
26391                 result.streetNumber = component.short_name;
26392             } else if (component.types.indexOf("route") >= 0) {
26393                 result.streetName = component.short_name;
26394             } else if (component.types.indexOf("neighborhood") >= 0) {
26395                 result.city = component.short_name;
26396             } else if (component.types.indexOf("locality") >= 0) {
26397                 result.city = component.short_name;
26398             } else if (component.types.indexOf("sublocality") >= 0) {
26399                 result.district = component.short_name;
26400             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26401                 result.stateOrProvince = component.short_name;
26402             } else if (component.types.indexOf("country") >= 0) {
26403                 result.country = component.short_name;
26404             }
26405         }
26406         
26407         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26408         result.addressLine2 = "";
26409         return result;
26410     },
26411     
26412     setZoomLevel: function(zoom)
26413     {
26414         this.gMapContext.map.setZoom(zoom);
26415     },
26416     
26417     show: function()
26418     {
26419         if(!this.el){
26420             return;
26421         }
26422         
26423         this.el.show();
26424         
26425         this.resize();
26426         
26427         this.fireEvent('show', this);
26428     },
26429     
26430     hide: function()
26431     {
26432         if(!this.el){
26433             return;
26434         }
26435         
26436         this.el.hide();
26437         
26438         this.fireEvent('hide', this);
26439     }
26440     
26441 });
26442
26443 Roo.apply(Roo.bootstrap.LocationPicker, {
26444     
26445     OverlayView : function(map, options)
26446     {
26447         options = options || {};
26448         
26449         this.setMap(map);
26450     }
26451     
26452     
26453 });/*
26454  * - LGPL
26455  *
26456  * Alert
26457  * 
26458  */
26459
26460 /**
26461  * @class Roo.bootstrap.Alert
26462  * @extends Roo.bootstrap.Component
26463  * Bootstrap Alert class
26464  * @cfg {String} title The title of alert
26465  * @cfg {String} html The content of alert
26466  * @cfg {String} weight (  success | info | warning | danger )
26467  * @cfg {String} faicon font-awesomeicon
26468  * 
26469  * @constructor
26470  * Create a new alert
26471  * @param {Object} config The config object
26472  */
26473
26474
26475 Roo.bootstrap.Alert = function(config){
26476     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26477     
26478 };
26479
26480 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26481     
26482     title: '',
26483     html: '',
26484     weight: false,
26485     faicon: false,
26486     
26487     getAutoCreate : function()
26488     {
26489         
26490         var cfg = {
26491             tag : 'div',
26492             cls : 'alert',
26493             cn : [
26494                 {
26495                     tag : 'i',
26496                     cls : 'roo-alert-icon'
26497                     
26498                 },
26499                 {
26500                     tag : 'b',
26501                     cls : 'roo-alert-title',
26502                     html : this.title
26503                 },
26504                 {
26505                     tag : 'span',
26506                     cls : 'roo-alert-text',
26507                     html : this.html
26508                 }
26509             ]
26510         };
26511         
26512         if(this.faicon){
26513             cfg.cn[0].cls += ' fa ' + this.faicon;
26514         }
26515         
26516         if(this.weight){
26517             cfg.cls += ' alert-' + this.weight;
26518         }
26519         
26520         return cfg;
26521     },
26522     
26523     initEvents: function() 
26524     {
26525         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26526     },
26527     
26528     setTitle : function(str)
26529     {
26530         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26531     },
26532     
26533     setText : function(str)
26534     {
26535         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26536     },
26537     
26538     setWeight : function(weight)
26539     {
26540         if(this.weight){
26541             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26542         }
26543         
26544         this.weight = weight;
26545         
26546         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26547     },
26548     
26549     setIcon : function(icon)
26550     {
26551         if(this.faicon){
26552             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26553         }
26554         
26555         this.faicon = icon;
26556         
26557         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26558     },
26559     
26560     hide: function() 
26561     {
26562         this.el.hide();   
26563     },
26564     
26565     show: function() 
26566     {  
26567         this.el.show();   
26568     }
26569     
26570 });
26571
26572  
26573 /*
26574 * Licence: LGPL
26575 */
26576
26577 /**
26578  * @class Roo.bootstrap.UploadCropbox
26579  * @extends Roo.bootstrap.Component
26580  * Bootstrap UploadCropbox class
26581  * @cfg {String} emptyText show when image has been loaded
26582  * @cfg {String} rotateNotify show when image too small to rotate
26583  * @cfg {Number} errorTimeout default 3000
26584  * @cfg {Number} minWidth default 300
26585  * @cfg {Number} minHeight default 300
26586  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26587  * @cfg {Boolean} isDocument (true|false) default false
26588  * @cfg {String} url action url
26589  * @cfg {String} paramName default 'imageUpload'
26590  * @cfg {String} method default POST
26591  * @cfg {Boolean} loadMask (true|false) default true
26592  * @cfg {Boolean} loadingText default 'Loading...'
26593  * 
26594  * @constructor
26595  * Create a new UploadCropbox
26596  * @param {Object} config The config object
26597  */
26598
26599 Roo.bootstrap.UploadCropbox = function(config){
26600     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26601     
26602     this.addEvents({
26603         /**
26604          * @event beforeselectfile
26605          * Fire before select file
26606          * @param {Roo.bootstrap.UploadCropbox} this
26607          */
26608         "beforeselectfile" : true,
26609         /**
26610          * @event initial
26611          * Fire after initEvent
26612          * @param {Roo.bootstrap.UploadCropbox} this
26613          */
26614         "initial" : true,
26615         /**
26616          * @event crop
26617          * Fire after initEvent
26618          * @param {Roo.bootstrap.UploadCropbox} this
26619          * @param {String} data
26620          */
26621         "crop" : true,
26622         /**
26623          * @event prepare
26624          * Fire when preparing the file data
26625          * @param {Roo.bootstrap.UploadCropbox} this
26626          * @param {Object} file
26627          */
26628         "prepare" : true,
26629         /**
26630          * @event exception
26631          * Fire when get exception
26632          * @param {Roo.bootstrap.UploadCropbox} this
26633          * @param {XMLHttpRequest} xhr
26634          */
26635         "exception" : true,
26636         /**
26637          * @event beforeloadcanvas
26638          * Fire before load the canvas
26639          * @param {Roo.bootstrap.UploadCropbox} this
26640          * @param {String} src
26641          */
26642         "beforeloadcanvas" : true,
26643         /**
26644          * @event trash
26645          * Fire when trash image
26646          * @param {Roo.bootstrap.UploadCropbox} this
26647          */
26648         "trash" : true,
26649         /**
26650          * @event download
26651          * Fire when download the image
26652          * @param {Roo.bootstrap.UploadCropbox} this
26653          */
26654         "download" : true,
26655         /**
26656          * @event footerbuttonclick
26657          * Fire when footerbuttonclick
26658          * @param {Roo.bootstrap.UploadCropbox} this
26659          * @param {String} type
26660          */
26661         "footerbuttonclick" : true,
26662         /**
26663          * @event resize
26664          * Fire when resize
26665          * @param {Roo.bootstrap.UploadCropbox} this
26666          */
26667         "resize" : true,
26668         /**
26669          * @event rotate
26670          * Fire when rotate the image
26671          * @param {Roo.bootstrap.UploadCropbox} this
26672          * @param {String} pos
26673          */
26674         "rotate" : true,
26675         /**
26676          * @event inspect
26677          * Fire when inspect the file
26678          * @param {Roo.bootstrap.UploadCropbox} this
26679          * @param {Object} file
26680          */
26681         "inspect" : true,
26682         /**
26683          * @event upload
26684          * Fire when xhr upload the file
26685          * @param {Roo.bootstrap.UploadCropbox} this
26686          * @param {Object} data
26687          */
26688         "upload" : true,
26689         /**
26690          * @event arrange
26691          * Fire when arrange the file data
26692          * @param {Roo.bootstrap.UploadCropbox} this
26693          * @param {Object} formData
26694          */
26695         "arrange" : true
26696     });
26697     
26698     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26699 };
26700
26701 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26702     
26703     emptyText : 'Click to upload image',
26704     rotateNotify : 'Image is too small to rotate',
26705     errorTimeout : 3000,
26706     scale : 0,
26707     baseScale : 1,
26708     rotate : 0,
26709     dragable : false,
26710     pinching : false,
26711     mouseX : 0,
26712     mouseY : 0,
26713     cropData : false,
26714     minWidth : 300,
26715     minHeight : 300,
26716     file : false,
26717     exif : {},
26718     baseRotate : 1,
26719     cropType : 'image/jpeg',
26720     buttons : false,
26721     canvasLoaded : false,
26722     isDocument : false,
26723     method : 'POST',
26724     paramName : 'imageUpload',
26725     loadMask : true,
26726     loadingText : 'Loading...',
26727     maskEl : false,
26728     
26729     getAutoCreate : function()
26730     {
26731         var cfg = {
26732             tag : 'div',
26733             cls : 'roo-upload-cropbox',
26734             cn : [
26735                 {
26736                     tag : 'input',
26737                     cls : 'roo-upload-cropbox-selector',
26738                     type : 'file'
26739                 },
26740                 {
26741                     tag : 'div',
26742                     cls : 'roo-upload-cropbox-body',
26743                     style : 'cursor:pointer',
26744                     cn : [
26745                         {
26746                             tag : 'div',
26747                             cls : 'roo-upload-cropbox-preview'
26748                         },
26749                         {
26750                             tag : 'div',
26751                             cls : 'roo-upload-cropbox-thumb'
26752                         },
26753                         {
26754                             tag : 'div',
26755                             cls : 'roo-upload-cropbox-empty-notify',
26756                             html : this.emptyText
26757                         },
26758                         {
26759                             tag : 'div',
26760                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26761                             html : this.rotateNotify
26762                         }
26763                     ]
26764                 },
26765                 {
26766                     tag : 'div',
26767                     cls : 'roo-upload-cropbox-footer',
26768                     cn : {
26769                         tag : 'div',
26770                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26771                         cn : []
26772                     }
26773                 }
26774             ]
26775         };
26776         
26777         return cfg;
26778     },
26779     
26780     onRender : function(ct, position)
26781     {
26782         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26783         
26784         if (this.buttons.length) {
26785             
26786             Roo.each(this.buttons, function(bb) {
26787                 
26788                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26789                 
26790                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26791                 
26792             }, this);
26793         }
26794         
26795         if(this.loadMask){
26796             this.maskEl = this.el;
26797         }
26798     },
26799     
26800     initEvents : function()
26801     {
26802         this.urlAPI = (window.createObjectURL && window) || 
26803                                 (window.URL && URL.revokeObjectURL && URL) || 
26804                                 (window.webkitURL && webkitURL);
26805                         
26806         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26807         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26808         
26809         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26810         this.selectorEl.hide();
26811         
26812         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26813         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26814         
26815         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26816         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26817         this.thumbEl.hide();
26818         
26819         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26820         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26821         
26822         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26823         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26824         this.errorEl.hide();
26825         
26826         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26827         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26828         this.footerEl.hide();
26829         
26830         this.setThumbBoxSize();
26831         
26832         this.bind();
26833         
26834         this.resize();
26835         
26836         this.fireEvent('initial', this);
26837     },
26838
26839     bind : function()
26840     {
26841         var _this = this;
26842         
26843         window.addEventListener("resize", function() { _this.resize(); } );
26844         
26845         this.bodyEl.on('click', this.beforeSelectFile, this);
26846         
26847         if(Roo.isTouch){
26848             this.bodyEl.on('touchstart', this.onTouchStart, this);
26849             this.bodyEl.on('touchmove', this.onTouchMove, this);
26850             this.bodyEl.on('touchend', this.onTouchEnd, this);
26851         }
26852         
26853         if(!Roo.isTouch){
26854             this.bodyEl.on('mousedown', this.onMouseDown, this);
26855             this.bodyEl.on('mousemove', this.onMouseMove, this);
26856             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26857             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26858             Roo.get(document).on('mouseup', this.onMouseUp, this);
26859         }
26860         
26861         this.selectorEl.on('change', this.onFileSelected, this);
26862     },
26863     
26864     reset : function()
26865     {    
26866         this.scale = 0;
26867         this.baseScale = 1;
26868         this.rotate = 0;
26869         this.baseRotate = 1;
26870         this.dragable = false;
26871         this.pinching = false;
26872         this.mouseX = 0;
26873         this.mouseY = 0;
26874         this.cropData = false;
26875         this.notifyEl.dom.innerHTML = this.emptyText;
26876         
26877         this.selectorEl.dom.value = '';
26878         
26879     },
26880     
26881     resize : function()
26882     {
26883         if(this.fireEvent('resize', this) != false){
26884             this.setThumbBoxPosition();
26885             this.setCanvasPosition();
26886         }
26887     },
26888     
26889     onFooterButtonClick : function(e, el, o, type)
26890     {
26891         switch (type) {
26892             case 'rotate-left' :
26893                 this.onRotateLeft(e);
26894                 break;
26895             case 'rotate-right' :
26896                 this.onRotateRight(e);
26897                 break;
26898             case 'picture' :
26899                 this.beforeSelectFile(e);
26900                 break;
26901             case 'trash' :
26902                 this.trash(e);
26903                 break;
26904             case 'crop' :
26905                 this.crop(e);
26906                 break;
26907             case 'download' :
26908                 this.download(e);
26909                 break;
26910             default :
26911                 break;
26912         }
26913         
26914         this.fireEvent('footerbuttonclick', this, type);
26915     },
26916     
26917     beforeSelectFile : function(e)
26918     {
26919         e.preventDefault();
26920         
26921         if(this.fireEvent('beforeselectfile', this) != false){
26922             this.selectorEl.dom.click();
26923         }
26924     },
26925     
26926     onFileSelected : function(e)
26927     {
26928         e.preventDefault();
26929         
26930         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26931             return;
26932         }
26933         
26934         var file = this.selectorEl.dom.files[0];
26935         
26936         if(this.fireEvent('inspect', this, file) != false){
26937             this.prepare(file);
26938         }
26939         
26940     },
26941     
26942     trash : function(e)
26943     {
26944         this.fireEvent('trash', this);
26945     },
26946     
26947     download : function(e)
26948     {
26949         this.fireEvent('download', this);
26950     },
26951     
26952     loadCanvas : function(src)
26953     {   
26954         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26955             
26956             this.reset();
26957             
26958             this.imageEl = document.createElement('img');
26959             
26960             var _this = this;
26961             
26962             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26963             
26964             this.imageEl.src = src;
26965         }
26966     },
26967     
26968     onLoadCanvas : function()
26969     {   
26970         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26971         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26972         
26973         this.bodyEl.un('click', this.beforeSelectFile, this);
26974         
26975         this.notifyEl.hide();
26976         this.thumbEl.show();
26977         this.footerEl.show();
26978         
26979         this.baseRotateLevel();
26980         
26981         if(this.isDocument){
26982             this.setThumbBoxSize();
26983         }
26984         
26985         this.setThumbBoxPosition();
26986         
26987         this.baseScaleLevel();
26988         
26989         this.draw();
26990         
26991         this.resize();
26992         
26993         this.canvasLoaded = true;
26994         
26995         if(this.loadMask){
26996             this.maskEl.unmask();
26997         }
26998         
26999     },
27000     
27001     setCanvasPosition : function()
27002     {   
27003         if(!this.canvasEl){
27004             return;
27005         }
27006         
27007         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27008         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27009         
27010         this.previewEl.setLeft(pw);
27011         this.previewEl.setTop(ph);
27012         
27013     },
27014     
27015     onMouseDown : function(e)
27016     {   
27017         e.stopEvent();
27018         
27019         this.dragable = true;
27020         this.pinching = false;
27021         
27022         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27023             this.dragable = false;
27024             return;
27025         }
27026         
27027         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27028         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27029         
27030     },
27031     
27032     onMouseMove : function(e)
27033     {   
27034         e.stopEvent();
27035         
27036         if(!this.canvasLoaded){
27037             return;
27038         }
27039         
27040         if (!this.dragable){
27041             return;
27042         }
27043         
27044         var minX = Math.ceil(this.thumbEl.getLeft(true));
27045         var minY = Math.ceil(this.thumbEl.getTop(true));
27046         
27047         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27048         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27049         
27050         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27051         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27052         
27053         x = x - this.mouseX;
27054         y = y - this.mouseY;
27055         
27056         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27057         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27058         
27059         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27060         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27061         
27062         this.previewEl.setLeft(bgX);
27063         this.previewEl.setTop(bgY);
27064         
27065         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27066         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27067     },
27068     
27069     onMouseUp : function(e)
27070     {   
27071         e.stopEvent();
27072         
27073         this.dragable = false;
27074     },
27075     
27076     onMouseWheel : function(e)
27077     {   
27078         e.stopEvent();
27079         
27080         this.startScale = this.scale;
27081         
27082         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27083         
27084         if(!this.zoomable()){
27085             this.scale = this.startScale;
27086             return;
27087         }
27088         
27089         this.draw();
27090         
27091         return;
27092     },
27093     
27094     zoomable : function()
27095     {
27096         var minScale = this.thumbEl.getWidth() / this.minWidth;
27097         
27098         if(this.minWidth < this.minHeight){
27099             minScale = this.thumbEl.getHeight() / this.minHeight;
27100         }
27101         
27102         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27103         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27104         
27105         if(
27106                 this.isDocument &&
27107                 (this.rotate == 0 || this.rotate == 180) && 
27108                 (
27109                     width > this.imageEl.OriginWidth || 
27110                     height > this.imageEl.OriginHeight ||
27111                     (width < this.minWidth && height < this.minHeight)
27112                 )
27113         ){
27114             return false;
27115         }
27116         
27117         if(
27118                 this.isDocument &&
27119                 (this.rotate == 90 || this.rotate == 270) && 
27120                 (
27121                     width > this.imageEl.OriginWidth || 
27122                     height > this.imageEl.OriginHeight ||
27123                     (width < this.minHeight && height < this.minWidth)
27124                 )
27125         ){
27126             return false;
27127         }
27128         
27129         if(
27130                 !this.isDocument &&
27131                 (this.rotate == 0 || this.rotate == 180) && 
27132                 (
27133                     width < this.minWidth || 
27134                     width > this.imageEl.OriginWidth || 
27135                     height < this.minHeight || 
27136                     height > this.imageEl.OriginHeight
27137                 )
27138         ){
27139             return false;
27140         }
27141         
27142         if(
27143                 !this.isDocument &&
27144                 (this.rotate == 90 || this.rotate == 270) && 
27145                 (
27146                     width < this.minHeight || 
27147                     width > this.imageEl.OriginWidth || 
27148                     height < this.minWidth || 
27149                     height > this.imageEl.OriginHeight
27150                 )
27151         ){
27152             return false;
27153         }
27154         
27155         return true;
27156         
27157     },
27158     
27159     onRotateLeft : function(e)
27160     {   
27161         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27162             
27163             var minScale = this.thumbEl.getWidth() / this.minWidth;
27164             
27165             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27166             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27167             
27168             this.startScale = this.scale;
27169             
27170             while (this.getScaleLevel() < minScale){
27171             
27172                 this.scale = this.scale + 1;
27173                 
27174                 if(!this.zoomable()){
27175                     break;
27176                 }
27177                 
27178                 if(
27179                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27180                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27181                 ){
27182                     continue;
27183                 }
27184                 
27185                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27186
27187                 this.draw();
27188                 
27189                 return;
27190             }
27191             
27192             this.scale = this.startScale;
27193             
27194             this.onRotateFail();
27195             
27196             return false;
27197         }
27198         
27199         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27200
27201         if(this.isDocument){
27202             this.setThumbBoxSize();
27203             this.setThumbBoxPosition();
27204             this.setCanvasPosition();
27205         }
27206         
27207         this.draw();
27208         
27209         this.fireEvent('rotate', this, 'left');
27210         
27211     },
27212     
27213     onRotateRight : function(e)
27214     {
27215         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27216             
27217             var minScale = this.thumbEl.getWidth() / this.minWidth;
27218         
27219             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27220             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27221             
27222             this.startScale = this.scale;
27223             
27224             while (this.getScaleLevel() < minScale){
27225             
27226                 this.scale = this.scale + 1;
27227                 
27228                 if(!this.zoomable()){
27229                     break;
27230                 }
27231                 
27232                 if(
27233                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27234                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27235                 ){
27236                     continue;
27237                 }
27238                 
27239                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27240
27241                 this.draw();
27242                 
27243                 return;
27244             }
27245             
27246             this.scale = this.startScale;
27247             
27248             this.onRotateFail();
27249             
27250             return false;
27251         }
27252         
27253         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27254
27255         if(this.isDocument){
27256             this.setThumbBoxSize();
27257             this.setThumbBoxPosition();
27258             this.setCanvasPosition();
27259         }
27260         
27261         this.draw();
27262         
27263         this.fireEvent('rotate', this, 'right');
27264     },
27265     
27266     onRotateFail : function()
27267     {
27268         this.errorEl.show(true);
27269         
27270         var _this = this;
27271         
27272         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27273     },
27274     
27275     draw : function()
27276     {
27277         this.previewEl.dom.innerHTML = '';
27278         
27279         var canvasEl = document.createElement("canvas");
27280         
27281         var contextEl = canvasEl.getContext("2d");
27282         
27283         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27284         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27285         var center = this.imageEl.OriginWidth / 2;
27286         
27287         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27288             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27289             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27290             center = this.imageEl.OriginHeight / 2;
27291         }
27292         
27293         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27294         
27295         contextEl.translate(center, center);
27296         contextEl.rotate(this.rotate * Math.PI / 180);
27297
27298         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27299         
27300         this.canvasEl = document.createElement("canvas");
27301         
27302         this.contextEl = this.canvasEl.getContext("2d");
27303         
27304         switch (this.rotate) {
27305             case 0 :
27306                 
27307                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27308                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27309                 
27310                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27311                 
27312                 break;
27313             case 90 : 
27314                 
27315                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27316                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27317                 
27318                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27319                     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);
27320                     break;
27321                 }
27322                 
27323                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27324                 
27325                 break;
27326             case 180 :
27327                 
27328                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27329                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27330                 
27331                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27332                     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);
27333                     break;
27334                 }
27335                 
27336                 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);
27337                 
27338                 break;
27339             case 270 :
27340                 
27341                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27342                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27343         
27344                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27345                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27346                     break;
27347                 }
27348                 
27349                 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);
27350                 
27351                 break;
27352             default : 
27353                 break;
27354         }
27355         
27356         this.previewEl.appendChild(this.canvasEl);
27357         
27358         this.setCanvasPosition();
27359     },
27360     
27361     crop : function()
27362     {
27363         if(!this.canvasLoaded){
27364             return;
27365         }
27366         
27367         var imageCanvas = document.createElement("canvas");
27368         
27369         var imageContext = imageCanvas.getContext("2d");
27370         
27371         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27372         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27373         
27374         var center = imageCanvas.width / 2;
27375         
27376         imageContext.translate(center, center);
27377         
27378         imageContext.rotate(this.rotate * Math.PI / 180);
27379         
27380         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27381         
27382         var canvas = document.createElement("canvas");
27383         
27384         var context = canvas.getContext("2d");
27385                 
27386         canvas.width = this.minWidth;
27387         canvas.height = this.minHeight;
27388
27389         switch (this.rotate) {
27390             case 0 :
27391                 
27392                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27393                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27394                 
27395                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27396                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27397                 
27398                 var targetWidth = this.minWidth - 2 * x;
27399                 var targetHeight = this.minHeight - 2 * y;
27400                 
27401                 var scale = 1;
27402                 
27403                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27404                     scale = targetWidth / width;
27405                 }
27406                 
27407                 if(x > 0 && y == 0){
27408                     scale = targetHeight / height;
27409                 }
27410                 
27411                 if(x > 0 && y > 0){
27412                     scale = targetWidth / width;
27413                     
27414                     if(width < height){
27415                         scale = targetHeight / height;
27416                     }
27417                 }
27418                 
27419                 context.scale(scale, scale);
27420                 
27421                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27422                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27423
27424                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27425                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27426
27427                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27428                 
27429                 break;
27430             case 90 : 
27431                 
27432                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27433                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27434                 
27435                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27436                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27437                 
27438                 var targetWidth = this.minWidth - 2 * x;
27439                 var targetHeight = this.minHeight - 2 * y;
27440                 
27441                 var scale = 1;
27442                 
27443                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27444                     scale = targetWidth / width;
27445                 }
27446                 
27447                 if(x > 0 && y == 0){
27448                     scale = targetHeight / height;
27449                 }
27450                 
27451                 if(x > 0 && y > 0){
27452                     scale = targetWidth / width;
27453                     
27454                     if(width < height){
27455                         scale = targetHeight / height;
27456                     }
27457                 }
27458                 
27459                 context.scale(scale, scale);
27460                 
27461                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27462                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27463
27464                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27465                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27466                 
27467                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27468                 
27469                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27470                 
27471                 break;
27472             case 180 :
27473                 
27474                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27475                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27476                 
27477                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27478                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27479                 
27480                 var targetWidth = this.minWidth - 2 * x;
27481                 var targetHeight = this.minHeight - 2 * y;
27482                 
27483                 var scale = 1;
27484                 
27485                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27486                     scale = targetWidth / width;
27487                 }
27488                 
27489                 if(x > 0 && y == 0){
27490                     scale = targetHeight / height;
27491                 }
27492                 
27493                 if(x > 0 && y > 0){
27494                     scale = targetWidth / width;
27495                     
27496                     if(width < height){
27497                         scale = targetHeight / height;
27498                     }
27499                 }
27500                 
27501                 context.scale(scale, scale);
27502                 
27503                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27504                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27505
27506                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27507                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27508
27509                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27510                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27511                 
27512                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27513                 
27514                 break;
27515             case 270 :
27516                 
27517                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27518                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27519                 
27520                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27521                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27522                 
27523                 var targetWidth = this.minWidth - 2 * x;
27524                 var targetHeight = this.minHeight - 2 * y;
27525                 
27526                 var scale = 1;
27527                 
27528                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27529                     scale = targetWidth / width;
27530                 }
27531                 
27532                 if(x > 0 && y == 0){
27533                     scale = targetHeight / height;
27534                 }
27535                 
27536                 if(x > 0 && y > 0){
27537                     scale = targetWidth / width;
27538                     
27539                     if(width < height){
27540                         scale = targetHeight / height;
27541                     }
27542                 }
27543                 
27544                 context.scale(scale, scale);
27545                 
27546                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27547                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27548
27549                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27550                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27551                 
27552                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27553                 
27554                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27555                 
27556                 break;
27557             default : 
27558                 break;
27559         }
27560         
27561         this.cropData = canvas.toDataURL(this.cropType);
27562         
27563         if(this.fireEvent('crop', this, this.cropData) !== false){
27564             this.process(this.file, this.cropData);
27565         }
27566         
27567         return;
27568         
27569     },
27570     
27571     setThumbBoxSize : function()
27572     {
27573         var width, height;
27574         
27575         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27576             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27577             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27578             
27579             this.minWidth = width;
27580             this.minHeight = height;
27581             
27582             if(this.rotate == 90 || this.rotate == 270){
27583                 this.minWidth = height;
27584                 this.minHeight = width;
27585             }
27586         }
27587         
27588         height = 300;
27589         width = Math.ceil(this.minWidth * height / this.minHeight);
27590         
27591         if(this.minWidth > this.minHeight){
27592             width = 300;
27593             height = Math.ceil(this.minHeight * width / this.minWidth);
27594         }
27595         
27596         this.thumbEl.setStyle({
27597             width : width + 'px',
27598             height : height + 'px'
27599         });
27600
27601         return;
27602             
27603     },
27604     
27605     setThumbBoxPosition : function()
27606     {
27607         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27608         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27609         
27610         this.thumbEl.setLeft(x);
27611         this.thumbEl.setTop(y);
27612         
27613     },
27614     
27615     baseRotateLevel : function()
27616     {
27617         this.baseRotate = 1;
27618         
27619         if(
27620                 typeof(this.exif) != 'undefined' &&
27621                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27622                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27623         ){
27624             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27625         }
27626         
27627         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27628         
27629     },
27630     
27631     baseScaleLevel : function()
27632     {
27633         var width, height;
27634         
27635         if(this.isDocument){
27636             
27637             if(this.baseRotate == 6 || this.baseRotate == 8){
27638             
27639                 height = this.thumbEl.getHeight();
27640                 this.baseScale = height / this.imageEl.OriginWidth;
27641
27642                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27643                     width = this.thumbEl.getWidth();
27644                     this.baseScale = width / this.imageEl.OriginHeight;
27645                 }
27646
27647                 return;
27648             }
27649
27650             height = this.thumbEl.getHeight();
27651             this.baseScale = height / this.imageEl.OriginHeight;
27652
27653             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27654                 width = this.thumbEl.getWidth();
27655                 this.baseScale = width / this.imageEl.OriginWidth;
27656             }
27657
27658             return;
27659         }
27660         
27661         if(this.baseRotate == 6 || this.baseRotate == 8){
27662             
27663             width = this.thumbEl.getHeight();
27664             this.baseScale = width / this.imageEl.OriginHeight;
27665             
27666             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27667                 height = this.thumbEl.getWidth();
27668                 this.baseScale = height / this.imageEl.OriginHeight;
27669             }
27670             
27671             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27672                 height = this.thumbEl.getWidth();
27673                 this.baseScale = height / this.imageEl.OriginHeight;
27674                 
27675                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27676                     width = this.thumbEl.getHeight();
27677                     this.baseScale = width / this.imageEl.OriginWidth;
27678                 }
27679             }
27680             
27681             return;
27682         }
27683         
27684         width = this.thumbEl.getWidth();
27685         this.baseScale = width / this.imageEl.OriginWidth;
27686         
27687         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27688             height = this.thumbEl.getHeight();
27689             this.baseScale = height / this.imageEl.OriginHeight;
27690         }
27691         
27692         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27693             
27694             height = this.thumbEl.getHeight();
27695             this.baseScale = height / this.imageEl.OriginHeight;
27696             
27697             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27698                 width = this.thumbEl.getWidth();
27699                 this.baseScale = width / this.imageEl.OriginWidth;
27700             }
27701             
27702         }
27703         
27704         return;
27705     },
27706     
27707     getScaleLevel : function()
27708     {
27709         return this.baseScale * Math.pow(1.1, this.scale);
27710     },
27711     
27712     onTouchStart : function(e)
27713     {
27714         if(!this.canvasLoaded){
27715             this.beforeSelectFile(e);
27716             return;
27717         }
27718         
27719         var touches = e.browserEvent.touches;
27720         
27721         if(!touches){
27722             return;
27723         }
27724         
27725         if(touches.length == 1){
27726             this.onMouseDown(e);
27727             return;
27728         }
27729         
27730         if(touches.length != 2){
27731             return;
27732         }
27733         
27734         var coords = [];
27735         
27736         for(var i = 0, finger; finger = touches[i]; i++){
27737             coords.push(finger.pageX, finger.pageY);
27738         }
27739         
27740         var x = Math.pow(coords[0] - coords[2], 2);
27741         var y = Math.pow(coords[1] - coords[3], 2);
27742         
27743         this.startDistance = Math.sqrt(x + y);
27744         
27745         this.startScale = this.scale;
27746         
27747         this.pinching = true;
27748         this.dragable = false;
27749         
27750     },
27751     
27752     onTouchMove : function(e)
27753     {
27754         if(!this.pinching && !this.dragable){
27755             return;
27756         }
27757         
27758         var touches = e.browserEvent.touches;
27759         
27760         if(!touches){
27761             return;
27762         }
27763         
27764         if(this.dragable){
27765             this.onMouseMove(e);
27766             return;
27767         }
27768         
27769         var coords = [];
27770         
27771         for(var i = 0, finger; finger = touches[i]; i++){
27772             coords.push(finger.pageX, finger.pageY);
27773         }
27774         
27775         var x = Math.pow(coords[0] - coords[2], 2);
27776         var y = Math.pow(coords[1] - coords[3], 2);
27777         
27778         this.endDistance = Math.sqrt(x + y);
27779         
27780         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27781         
27782         if(!this.zoomable()){
27783             this.scale = this.startScale;
27784             return;
27785         }
27786         
27787         this.draw();
27788         
27789     },
27790     
27791     onTouchEnd : function(e)
27792     {
27793         this.pinching = false;
27794         this.dragable = false;
27795         
27796     },
27797     
27798     process : function(file, crop)
27799     {
27800         if(this.loadMask){
27801             this.maskEl.mask(this.loadingText);
27802         }
27803         
27804         this.xhr = new XMLHttpRequest();
27805         
27806         file.xhr = this.xhr;
27807
27808         this.xhr.open(this.method, this.url, true);
27809         
27810         var headers = {
27811             "Accept": "application/json",
27812             "Cache-Control": "no-cache",
27813             "X-Requested-With": "XMLHttpRequest"
27814         };
27815         
27816         for (var headerName in headers) {
27817             var headerValue = headers[headerName];
27818             if (headerValue) {
27819                 this.xhr.setRequestHeader(headerName, headerValue);
27820             }
27821         }
27822         
27823         var _this = this;
27824         
27825         this.xhr.onload = function()
27826         {
27827             _this.xhrOnLoad(_this.xhr);
27828         }
27829         
27830         this.xhr.onerror = function()
27831         {
27832             _this.xhrOnError(_this.xhr);
27833         }
27834         
27835         var formData = new FormData();
27836
27837         formData.append('returnHTML', 'NO');
27838         
27839         if(crop){
27840             formData.append('crop', crop);
27841         }
27842         
27843         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27844             formData.append(this.paramName, file, file.name);
27845         }
27846         
27847         if(typeof(file.filename) != 'undefined'){
27848             formData.append('filename', file.filename);
27849         }
27850         
27851         if(typeof(file.mimetype) != 'undefined'){
27852             formData.append('mimetype', file.mimetype);
27853         }
27854         
27855         if(this.fireEvent('arrange', this, formData) != false){
27856             this.xhr.send(formData);
27857         };
27858     },
27859     
27860     xhrOnLoad : function(xhr)
27861     {
27862         if(this.loadMask){
27863             this.maskEl.unmask();
27864         }
27865         
27866         if (xhr.readyState !== 4) {
27867             this.fireEvent('exception', this, xhr);
27868             return;
27869         }
27870
27871         var response = Roo.decode(xhr.responseText);
27872         
27873         if(!response.success){
27874             this.fireEvent('exception', this, xhr);
27875             return;
27876         }
27877         
27878         var response = Roo.decode(xhr.responseText);
27879         
27880         this.fireEvent('upload', this, response);
27881         
27882     },
27883     
27884     xhrOnError : function()
27885     {
27886         if(this.loadMask){
27887             this.maskEl.unmask();
27888         }
27889         
27890         Roo.log('xhr on error');
27891         
27892         var response = Roo.decode(xhr.responseText);
27893           
27894         Roo.log(response);
27895         
27896     },
27897     
27898     prepare : function(file)
27899     {   
27900         if(this.loadMask){
27901             this.maskEl.mask(this.loadingText);
27902         }
27903         
27904         this.file = false;
27905         this.exif = {};
27906         
27907         if(typeof(file) === 'string'){
27908             this.loadCanvas(file);
27909             return;
27910         }
27911         
27912         if(!file || !this.urlAPI){
27913             return;
27914         }
27915         
27916         this.file = file;
27917         this.cropType = file.type;
27918         
27919         var _this = this;
27920         
27921         if(this.fireEvent('prepare', this, this.file) != false){
27922             
27923             var reader = new FileReader();
27924             
27925             reader.onload = function (e) {
27926                 if (e.target.error) {
27927                     Roo.log(e.target.error);
27928                     return;
27929                 }
27930                 
27931                 var buffer = e.target.result,
27932                     dataView = new DataView(buffer),
27933                     offset = 2,
27934                     maxOffset = dataView.byteLength - 4,
27935                     markerBytes,
27936                     markerLength;
27937                 
27938                 if (dataView.getUint16(0) === 0xffd8) {
27939                     while (offset < maxOffset) {
27940                         markerBytes = dataView.getUint16(offset);
27941                         
27942                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27943                             markerLength = dataView.getUint16(offset + 2) + 2;
27944                             if (offset + markerLength > dataView.byteLength) {
27945                                 Roo.log('Invalid meta data: Invalid segment size.');
27946                                 break;
27947                             }
27948                             
27949                             if(markerBytes == 0xffe1){
27950                                 _this.parseExifData(
27951                                     dataView,
27952                                     offset,
27953                                     markerLength
27954                                 );
27955                             }
27956                             
27957                             offset += markerLength;
27958                             
27959                             continue;
27960                         }
27961                         
27962                         break;
27963                     }
27964                     
27965                 }
27966                 
27967                 var url = _this.urlAPI.createObjectURL(_this.file);
27968                 
27969                 _this.loadCanvas(url);
27970                 
27971                 return;
27972             }
27973             
27974             reader.readAsArrayBuffer(this.file);
27975             
27976         }
27977         
27978     },
27979     
27980     parseExifData : function(dataView, offset, length)
27981     {
27982         var tiffOffset = offset + 10,
27983             littleEndian,
27984             dirOffset;
27985     
27986         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27987             // No Exif data, might be XMP data instead
27988             return;
27989         }
27990         
27991         // Check for the ASCII code for "Exif" (0x45786966):
27992         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27993             // No Exif data, might be XMP data instead
27994             return;
27995         }
27996         if (tiffOffset + 8 > dataView.byteLength) {
27997             Roo.log('Invalid Exif data: Invalid segment size.');
27998             return;
27999         }
28000         // Check for the two null bytes:
28001         if (dataView.getUint16(offset + 8) !== 0x0000) {
28002             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28003             return;
28004         }
28005         // Check the byte alignment:
28006         switch (dataView.getUint16(tiffOffset)) {
28007         case 0x4949:
28008             littleEndian = true;
28009             break;
28010         case 0x4D4D:
28011             littleEndian = false;
28012             break;
28013         default:
28014             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28015             return;
28016         }
28017         // Check for the TIFF tag marker (0x002A):
28018         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28019             Roo.log('Invalid Exif data: Missing TIFF marker.');
28020             return;
28021         }
28022         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28023         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28024         
28025         this.parseExifTags(
28026             dataView,
28027             tiffOffset,
28028             tiffOffset + dirOffset,
28029             littleEndian
28030         );
28031     },
28032     
28033     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28034     {
28035         var tagsNumber,
28036             dirEndOffset,
28037             i;
28038         if (dirOffset + 6 > dataView.byteLength) {
28039             Roo.log('Invalid Exif data: Invalid directory offset.');
28040             return;
28041         }
28042         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28043         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28044         if (dirEndOffset + 4 > dataView.byteLength) {
28045             Roo.log('Invalid Exif data: Invalid directory size.');
28046             return;
28047         }
28048         for (i = 0; i < tagsNumber; i += 1) {
28049             this.parseExifTag(
28050                 dataView,
28051                 tiffOffset,
28052                 dirOffset + 2 + 12 * i, // tag offset
28053                 littleEndian
28054             );
28055         }
28056         // Return the offset to the next directory:
28057         return dataView.getUint32(dirEndOffset, littleEndian);
28058     },
28059     
28060     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28061     {
28062         var tag = dataView.getUint16(offset, littleEndian);
28063         
28064         this.exif[tag] = this.getExifValue(
28065             dataView,
28066             tiffOffset,
28067             offset,
28068             dataView.getUint16(offset + 2, littleEndian), // tag type
28069             dataView.getUint32(offset + 4, littleEndian), // tag length
28070             littleEndian
28071         );
28072     },
28073     
28074     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28075     {
28076         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28077             tagSize,
28078             dataOffset,
28079             values,
28080             i,
28081             str,
28082             c;
28083     
28084         if (!tagType) {
28085             Roo.log('Invalid Exif data: Invalid tag type.');
28086             return;
28087         }
28088         
28089         tagSize = tagType.size * length;
28090         // Determine if the value is contained in the dataOffset bytes,
28091         // or if the value at the dataOffset is a pointer to the actual data:
28092         dataOffset = tagSize > 4 ?
28093                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28094         if (dataOffset + tagSize > dataView.byteLength) {
28095             Roo.log('Invalid Exif data: Invalid data offset.');
28096             return;
28097         }
28098         if (length === 1) {
28099             return tagType.getValue(dataView, dataOffset, littleEndian);
28100         }
28101         values = [];
28102         for (i = 0; i < length; i += 1) {
28103             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28104         }
28105         
28106         if (tagType.ascii) {
28107             str = '';
28108             // Concatenate the chars:
28109             for (i = 0; i < values.length; i += 1) {
28110                 c = values[i];
28111                 // Ignore the terminating NULL byte(s):
28112                 if (c === '\u0000') {
28113                     break;
28114                 }
28115                 str += c;
28116             }
28117             return str;
28118         }
28119         return values;
28120     }
28121     
28122 });
28123
28124 Roo.apply(Roo.bootstrap.UploadCropbox, {
28125     tags : {
28126         'Orientation': 0x0112
28127     },
28128     
28129     Orientation: {
28130             1: 0, //'top-left',
28131 //            2: 'top-right',
28132             3: 180, //'bottom-right',
28133 //            4: 'bottom-left',
28134 //            5: 'left-top',
28135             6: 90, //'right-top',
28136 //            7: 'right-bottom',
28137             8: 270 //'left-bottom'
28138     },
28139     
28140     exifTagTypes : {
28141         // byte, 8-bit unsigned int:
28142         1: {
28143             getValue: function (dataView, dataOffset) {
28144                 return dataView.getUint8(dataOffset);
28145             },
28146             size: 1
28147         },
28148         // ascii, 8-bit byte:
28149         2: {
28150             getValue: function (dataView, dataOffset) {
28151                 return String.fromCharCode(dataView.getUint8(dataOffset));
28152             },
28153             size: 1,
28154             ascii: true
28155         },
28156         // short, 16 bit int:
28157         3: {
28158             getValue: function (dataView, dataOffset, littleEndian) {
28159                 return dataView.getUint16(dataOffset, littleEndian);
28160             },
28161             size: 2
28162         },
28163         // long, 32 bit int:
28164         4: {
28165             getValue: function (dataView, dataOffset, littleEndian) {
28166                 return dataView.getUint32(dataOffset, littleEndian);
28167             },
28168             size: 4
28169         },
28170         // rational = two long values, first is numerator, second is denominator:
28171         5: {
28172             getValue: function (dataView, dataOffset, littleEndian) {
28173                 return dataView.getUint32(dataOffset, littleEndian) /
28174                     dataView.getUint32(dataOffset + 4, littleEndian);
28175             },
28176             size: 8
28177         },
28178         // slong, 32 bit signed int:
28179         9: {
28180             getValue: function (dataView, dataOffset, littleEndian) {
28181                 return dataView.getInt32(dataOffset, littleEndian);
28182             },
28183             size: 4
28184         },
28185         // srational, two slongs, first is numerator, second is denominator:
28186         10: {
28187             getValue: function (dataView, dataOffset, littleEndian) {
28188                 return dataView.getInt32(dataOffset, littleEndian) /
28189                     dataView.getInt32(dataOffset + 4, littleEndian);
28190             },
28191             size: 8
28192         }
28193     },
28194     
28195     footer : {
28196         STANDARD : [
28197             {
28198                 tag : 'div',
28199                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28200                 action : 'rotate-left',
28201                 cn : [
28202                     {
28203                         tag : 'button',
28204                         cls : 'btn btn-default',
28205                         html : '<i class="fa fa-undo"></i>'
28206                     }
28207                 ]
28208             },
28209             {
28210                 tag : 'div',
28211                 cls : 'btn-group roo-upload-cropbox-picture',
28212                 action : 'picture',
28213                 cn : [
28214                     {
28215                         tag : 'button',
28216                         cls : 'btn btn-default',
28217                         html : '<i class="fa fa-picture-o"></i>'
28218                     }
28219                 ]
28220             },
28221             {
28222                 tag : 'div',
28223                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28224                 action : 'rotate-right',
28225                 cn : [
28226                     {
28227                         tag : 'button',
28228                         cls : 'btn btn-default',
28229                         html : '<i class="fa fa-repeat"></i>'
28230                     }
28231                 ]
28232             }
28233         ],
28234         DOCUMENT : [
28235             {
28236                 tag : 'div',
28237                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28238                 action : 'rotate-left',
28239                 cn : [
28240                     {
28241                         tag : 'button',
28242                         cls : 'btn btn-default',
28243                         html : '<i class="fa fa-undo"></i>'
28244                     }
28245                 ]
28246             },
28247             {
28248                 tag : 'div',
28249                 cls : 'btn-group roo-upload-cropbox-download',
28250                 action : 'download',
28251                 cn : [
28252                     {
28253                         tag : 'button',
28254                         cls : 'btn btn-default',
28255                         html : '<i class="fa fa-download"></i>'
28256                     }
28257                 ]
28258             },
28259             {
28260                 tag : 'div',
28261                 cls : 'btn-group roo-upload-cropbox-crop',
28262                 action : 'crop',
28263                 cn : [
28264                     {
28265                         tag : 'button',
28266                         cls : 'btn btn-default',
28267                         html : '<i class="fa fa-crop"></i>'
28268                     }
28269                 ]
28270             },
28271             {
28272                 tag : 'div',
28273                 cls : 'btn-group roo-upload-cropbox-trash',
28274                 action : 'trash',
28275                 cn : [
28276                     {
28277                         tag : 'button',
28278                         cls : 'btn btn-default',
28279                         html : '<i class="fa fa-trash"></i>'
28280                     }
28281                 ]
28282             },
28283             {
28284                 tag : 'div',
28285                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28286                 action : 'rotate-right',
28287                 cn : [
28288                     {
28289                         tag : 'button',
28290                         cls : 'btn btn-default',
28291                         html : '<i class="fa fa-repeat"></i>'
28292                     }
28293                 ]
28294             }
28295         ],
28296         ROTATOR : [
28297             {
28298                 tag : 'div',
28299                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28300                 action : 'rotate-left',
28301                 cn : [
28302                     {
28303                         tag : 'button',
28304                         cls : 'btn btn-default',
28305                         html : '<i class="fa fa-undo"></i>'
28306                     }
28307                 ]
28308             },
28309             {
28310                 tag : 'div',
28311                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28312                 action : 'rotate-right',
28313                 cn : [
28314                     {
28315                         tag : 'button',
28316                         cls : 'btn btn-default',
28317                         html : '<i class="fa fa-repeat"></i>'
28318                     }
28319                 ]
28320             }
28321         ]
28322     }
28323 });
28324
28325 /*
28326 * Licence: LGPL
28327 */
28328
28329 /**
28330  * @class Roo.bootstrap.DocumentManager
28331  * @extends Roo.bootstrap.Component
28332  * Bootstrap DocumentManager class
28333  * @cfg {String} paramName default 'imageUpload'
28334  * @cfg {String} toolTipName default 'filename'
28335  * @cfg {String} method default POST
28336  * @cfg {String} url action url
28337  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28338  * @cfg {Boolean} multiple multiple upload default true
28339  * @cfg {Number} thumbSize default 300
28340  * @cfg {String} fieldLabel
28341  * @cfg {Number} labelWidth default 4
28342  * @cfg {String} labelAlign (left|top) default left
28343  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28344 * @cfg {Number} labellg set the width of label (1-12)
28345  * @cfg {Number} labelmd set the width of label (1-12)
28346  * @cfg {Number} labelsm set the width of label (1-12)
28347  * @cfg {Number} labelxs set the width of label (1-12)
28348  * 
28349  * @constructor
28350  * Create a new DocumentManager
28351  * @param {Object} config The config object
28352  */
28353
28354 Roo.bootstrap.DocumentManager = function(config){
28355     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28356     
28357     this.files = [];
28358     this.delegates = [];
28359     
28360     this.addEvents({
28361         /**
28362          * @event initial
28363          * Fire when initial the DocumentManager
28364          * @param {Roo.bootstrap.DocumentManager} this
28365          */
28366         "initial" : true,
28367         /**
28368          * @event inspect
28369          * inspect selected file
28370          * @param {Roo.bootstrap.DocumentManager} this
28371          * @param {File} file
28372          */
28373         "inspect" : true,
28374         /**
28375          * @event exception
28376          * Fire when xhr load exception
28377          * @param {Roo.bootstrap.DocumentManager} this
28378          * @param {XMLHttpRequest} xhr
28379          */
28380         "exception" : true,
28381         /**
28382          * @event afterupload
28383          * Fire when xhr load exception
28384          * @param {Roo.bootstrap.DocumentManager} this
28385          * @param {XMLHttpRequest} xhr
28386          */
28387         "afterupload" : true,
28388         /**
28389          * @event prepare
28390          * prepare the form data
28391          * @param {Roo.bootstrap.DocumentManager} this
28392          * @param {Object} formData
28393          */
28394         "prepare" : true,
28395         /**
28396          * @event remove
28397          * Fire when remove the file
28398          * @param {Roo.bootstrap.DocumentManager} this
28399          * @param {Object} file
28400          */
28401         "remove" : true,
28402         /**
28403          * @event refresh
28404          * Fire after refresh the file
28405          * @param {Roo.bootstrap.DocumentManager} this
28406          */
28407         "refresh" : true,
28408         /**
28409          * @event click
28410          * Fire after click the image
28411          * @param {Roo.bootstrap.DocumentManager} this
28412          * @param {Object} file
28413          */
28414         "click" : true,
28415         /**
28416          * @event edit
28417          * Fire when upload a image and editable set to true
28418          * @param {Roo.bootstrap.DocumentManager} this
28419          * @param {Object} file
28420          */
28421         "edit" : true,
28422         /**
28423          * @event beforeselectfile
28424          * Fire before select file
28425          * @param {Roo.bootstrap.DocumentManager} this
28426          */
28427         "beforeselectfile" : true,
28428         /**
28429          * @event process
28430          * Fire before process file
28431          * @param {Roo.bootstrap.DocumentManager} this
28432          * @param {Object} file
28433          */
28434         "process" : true,
28435         /**
28436          * @event previewrendered
28437          * Fire when preview rendered
28438          * @param {Roo.bootstrap.DocumentManager} this
28439          * @param {Object} file
28440          */
28441         "previewrendered" : true
28442         
28443     });
28444 };
28445
28446 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28447     
28448     boxes : 0,
28449     inputName : '',
28450     thumbSize : 300,
28451     multiple : true,
28452     files : false,
28453     method : 'POST',
28454     url : '',
28455     paramName : 'imageUpload',
28456     toolTipName : 'filename',
28457     fieldLabel : '',
28458     labelWidth : 4,
28459     labelAlign : 'left',
28460     editable : true,
28461     delegates : false,
28462     xhr : false, 
28463     
28464     labellg : 0,
28465     labelmd : 0,
28466     labelsm : 0,
28467     labelxs : 0,
28468     
28469     getAutoCreate : function()
28470     {   
28471         var managerWidget = {
28472             tag : 'div',
28473             cls : 'roo-document-manager',
28474             cn : [
28475                 {
28476                     tag : 'input',
28477                     cls : 'roo-document-manager-selector',
28478                     type : 'file'
28479                 },
28480                 {
28481                     tag : 'div',
28482                     cls : 'roo-document-manager-uploader',
28483                     cn : [
28484                         {
28485                             tag : 'div',
28486                             cls : 'roo-document-manager-upload-btn',
28487                             html : '<i class="fa fa-plus"></i>'
28488                         }
28489                     ]
28490                     
28491                 }
28492             ]
28493         };
28494         
28495         var content = [
28496             {
28497                 tag : 'div',
28498                 cls : 'column col-md-12',
28499                 cn : managerWidget
28500             }
28501         ];
28502         
28503         if(this.fieldLabel.length){
28504             
28505             content = [
28506                 {
28507                     tag : 'div',
28508                     cls : 'column col-md-12',
28509                     html : this.fieldLabel
28510                 },
28511                 {
28512                     tag : 'div',
28513                     cls : 'column col-md-12',
28514                     cn : managerWidget
28515                 }
28516             ];
28517
28518             if(this.labelAlign == 'left'){
28519                 content = [
28520                     {
28521                         tag : 'div',
28522                         cls : 'column',
28523                         html : this.fieldLabel
28524                     },
28525                     {
28526                         tag : 'div',
28527                         cls : 'column',
28528                         cn : managerWidget
28529                     }
28530                 ];
28531                 
28532                 if(this.labelWidth > 12){
28533                     content[0].style = "width: " + this.labelWidth + 'px';
28534                 }
28535
28536                 if(this.labelWidth < 13 && this.labelmd == 0){
28537                     this.labelmd = this.labelWidth;
28538                 }
28539
28540                 if(this.labellg > 0){
28541                     content[0].cls += ' col-lg-' + this.labellg;
28542                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28543                 }
28544
28545                 if(this.labelmd > 0){
28546                     content[0].cls += ' col-md-' + this.labelmd;
28547                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28548                 }
28549
28550                 if(this.labelsm > 0){
28551                     content[0].cls += ' col-sm-' + this.labelsm;
28552                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28553                 }
28554
28555                 if(this.labelxs > 0){
28556                     content[0].cls += ' col-xs-' + this.labelxs;
28557                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28558                 }
28559                 
28560             }
28561         }
28562         
28563         var cfg = {
28564             tag : 'div',
28565             cls : 'row clearfix',
28566             cn : content
28567         };
28568         
28569         return cfg;
28570         
28571     },
28572     
28573     initEvents : function()
28574     {
28575         this.managerEl = this.el.select('.roo-document-manager', true).first();
28576         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28577         
28578         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28579         this.selectorEl.hide();
28580         
28581         if(this.multiple){
28582             this.selectorEl.attr('multiple', 'multiple');
28583         }
28584         
28585         this.selectorEl.on('change', this.onFileSelected, this);
28586         
28587         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28588         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28589         
28590         this.uploader.on('click', this.onUploaderClick, this);
28591         
28592         this.renderProgressDialog();
28593         
28594         var _this = this;
28595         
28596         window.addEventListener("resize", function() { _this.refresh(); } );
28597         
28598         this.fireEvent('initial', this);
28599     },
28600     
28601     renderProgressDialog : function()
28602     {
28603         var _this = this;
28604         
28605         this.progressDialog = new Roo.bootstrap.Modal({
28606             cls : 'roo-document-manager-progress-dialog',
28607             allow_close : false,
28608             title : '',
28609             buttons : [
28610                 {
28611                     name  :'cancel',
28612                     weight : 'danger',
28613                     html : 'Cancel'
28614                 }
28615             ], 
28616             listeners : { 
28617                 btnclick : function() {
28618                     _this.uploadCancel();
28619                     this.hide();
28620                 }
28621             }
28622         });
28623          
28624         this.progressDialog.render(Roo.get(document.body));
28625          
28626         this.progress = new Roo.bootstrap.Progress({
28627             cls : 'roo-document-manager-progress',
28628             active : true,
28629             striped : true
28630         });
28631         
28632         this.progress.render(this.progressDialog.getChildContainer());
28633         
28634         this.progressBar = new Roo.bootstrap.ProgressBar({
28635             cls : 'roo-document-manager-progress-bar',
28636             aria_valuenow : 0,
28637             aria_valuemin : 0,
28638             aria_valuemax : 12,
28639             panel : 'success'
28640         });
28641         
28642         this.progressBar.render(this.progress.getChildContainer());
28643     },
28644     
28645     onUploaderClick : function(e)
28646     {
28647         e.preventDefault();
28648      
28649         if(this.fireEvent('beforeselectfile', this) != false){
28650             this.selectorEl.dom.click();
28651         }
28652         
28653     },
28654     
28655     onFileSelected : function(e)
28656     {
28657         e.preventDefault();
28658         
28659         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28660             return;
28661         }
28662         
28663         Roo.each(this.selectorEl.dom.files, function(file){
28664             if(this.fireEvent('inspect', this, file) != false){
28665                 this.files.push(file);
28666             }
28667         }, this);
28668         
28669         this.queue();
28670         
28671     },
28672     
28673     queue : function()
28674     {
28675         this.selectorEl.dom.value = '';
28676         
28677         if(!this.files || !this.files.length){
28678             return;
28679         }
28680         
28681         if(this.boxes > 0 && this.files.length > this.boxes){
28682             this.files = this.files.slice(0, this.boxes);
28683         }
28684         
28685         this.uploader.show();
28686         
28687         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28688             this.uploader.hide();
28689         }
28690         
28691         var _this = this;
28692         
28693         var files = [];
28694         
28695         var docs = [];
28696         
28697         Roo.each(this.files, function(file){
28698             
28699             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28700                 var f = this.renderPreview(file);
28701                 files.push(f);
28702                 return;
28703             }
28704             
28705             if(file.type.indexOf('image') != -1){
28706                 this.delegates.push(
28707                     (function(){
28708                         _this.process(file);
28709                     }).createDelegate(this)
28710                 );
28711         
28712                 return;
28713             }
28714             
28715             docs.push(
28716                 (function(){
28717                     _this.process(file);
28718                 }).createDelegate(this)
28719             );
28720             
28721         }, this);
28722         
28723         this.files = files;
28724         
28725         this.delegates = this.delegates.concat(docs);
28726         
28727         if(!this.delegates.length){
28728             this.refresh();
28729             return;
28730         }
28731         
28732         this.progressBar.aria_valuemax = this.delegates.length;
28733         
28734         this.arrange();
28735         
28736         return;
28737     },
28738     
28739     arrange : function()
28740     {
28741         if(!this.delegates.length){
28742             this.progressDialog.hide();
28743             this.refresh();
28744             return;
28745         }
28746         
28747         var delegate = this.delegates.shift();
28748         
28749         this.progressDialog.show();
28750         
28751         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28752         
28753         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28754         
28755         delegate();
28756     },
28757     
28758     refresh : function()
28759     {
28760         this.uploader.show();
28761         
28762         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28763             this.uploader.hide();
28764         }
28765         
28766         Roo.isTouch ? this.closable(false) : this.closable(true);
28767         
28768         this.fireEvent('refresh', this);
28769     },
28770     
28771     onRemove : function(e, el, o)
28772     {
28773         e.preventDefault();
28774         
28775         this.fireEvent('remove', this, o);
28776         
28777     },
28778     
28779     remove : function(o)
28780     {
28781         var files = [];
28782         
28783         Roo.each(this.files, function(file){
28784             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28785                 files.push(file);
28786                 return;
28787             }
28788
28789             o.target.remove();
28790
28791         }, this);
28792         
28793         this.files = files;
28794         
28795         this.refresh();
28796     },
28797     
28798     clear : function()
28799     {
28800         Roo.each(this.files, function(file){
28801             if(!file.target){
28802                 return;
28803             }
28804             
28805             file.target.remove();
28806
28807         }, this);
28808         
28809         this.files = [];
28810         
28811         this.refresh();
28812     },
28813     
28814     onClick : function(e, el, o)
28815     {
28816         e.preventDefault();
28817         
28818         this.fireEvent('click', this, o);
28819         
28820     },
28821     
28822     closable : function(closable)
28823     {
28824         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28825             
28826             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28827             
28828             if(closable){
28829                 el.show();
28830                 return;
28831             }
28832             
28833             el.hide();
28834             
28835         }, this);
28836     },
28837     
28838     xhrOnLoad : function(xhr)
28839     {
28840         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28841             el.remove();
28842         }, this);
28843         
28844         if (xhr.readyState !== 4) {
28845             this.arrange();
28846             this.fireEvent('exception', this, xhr);
28847             return;
28848         }
28849
28850         var response = Roo.decode(xhr.responseText);
28851         
28852         if(!response.success){
28853             this.arrange();
28854             this.fireEvent('exception', this, xhr);
28855             return;
28856         }
28857         
28858         var file = this.renderPreview(response.data);
28859         
28860         this.files.push(file);
28861         
28862         this.arrange();
28863         
28864         this.fireEvent('afterupload', this, xhr);
28865         
28866     },
28867     
28868     xhrOnError : function(xhr)
28869     {
28870         Roo.log('xhr on error');
28871         
28872         var response = Roo.decode(xhr.responseText);
28873           
28874         Roo.log(response);
28875         
28876         this.arrange();
28877     },
28878     
28879     process : function(file)
28880     {
28881         if(this.fireEvent('process', this, file) !== false){
28882             if(this.editable && file.type.indexOf('image') != -1){
28883                 this.fireEvent('edit', this, file);
28884                 return;
28885             }
28886
28887             this.uploadStart(file, false);
28888
28889             return;
28890         }
28891         
28892     },
28893     
28894     uploadStart : function(file, crop)
28895     {
28896         this.xhr = new XMLHttpRequest();
28897         
28898         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28899             this.arrange();
28900             return;
28901         }
28902         
28903         file.xhr = this.xhr;
28904             
28905         this.managerEl.createChild({
28906             tag : 'div',
28907             cls : 'roo-document-manager-loading',
28908             cn : [
28909                 {
28910                     tag : 'div',
28911                     tooltip : file.name,
28912                     cls : 'roo-document-manager-thumb',
28913                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28914                 }
28915             ]
28916
28917         });
28918
28919         this.xhr.open(this.method, this.url, true);
28920         
28921         var headers = {
28922             "Accept": "application/json",
28923             "Cache-Control": "no-cache",
28924             "X-Requested-With": "XMLHttpRequest"
28925         };
28926         
28927         for (var headerName in headers) {
28928             var headerValue = headers[headerName];
28929             if (headerValue) {
28930                 this.xhr.setRequestHeader(headerName, headerValue);
28931             }
28932         }
28933         
28934         var _this = this;
28935         
28936         this.xhr.onload = function()
28937         {
28938             _this.xhrOnLoad(_this.xhr);
28939         }
28940         
28941         this.xhr.onerror = function()
28942         {
28943             _this.xhrOnError(_this.xhr);
28944         }
28945         
28946         var formData = new FormData();
28947
28948         formData.append('returnHTML', 'NO');
28949         
28950         if(crop){
28951             formData.append('crop', crop);
28952         }
28953         
28954         formData.append(this.paramName, file, file.name);
28955         
28956         var options = {
28957             file : file, 
28958             manually : false
28959         };
28960         
28961         if(this.fireEvent('prepare', this, formData, options) != false){
28962             
28963             if(options.manually){
28964                 return;
28965             }
28966             
28967             this.xhr.send(formData);
28968             return;
28969         };
28970         
28971         this.uploadCancel();
28972     },
28973     
28974     uploadCancel : function()
28975     {
28976         if (this.xhr) {
28977             this.xhr.abort();
28978         }
28979         
28980         this.delegates = [];
28981         
28982         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28983             el.remove();
28984         }, this);
28985         
28986         this.arrange();
28987     },
28988     
28989     renderPreview : function(file)
28990     {
28991         if(typeof(file.target) != 'undefined' && file.target){
28992             return file;
28993         }
28994         
28995         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28996         
28997         var previewEl = this.managerEl.createChild({
28998             tag : 'div',
28999             cls : 'roo-document-manager-preview',
29000             cn : [
29001                 {
29002                     tag : 'div',
29003                     tooltip : file[this.toolTipName],
29004                     cls : 'roo-document-manager-thumb',
29005                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29006                 },
29007                 {
29008                     tag : 'button',
29009                     cls : 'close',
29010                     html : '<i class="fa fa-times-circle"></i>'
29011                 }
29012             ]
29013         });
29014
29015         var close = previewEl.select('button.close', true).first();
29016
29017         close.on('click', this.onRemove, this, file);
29018
29019         file.target = previewEl;
29020
29021         var image = previewEl.select('img', true).first();
29022         
29023         var _this = this;
29024         
29025         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29026         
29027         image.on('click', this.onClick, this, file);
29028         
29029         this.fireEvent('previewrendered', this, file);
29030         
29031         return file;
29032         
29033     },
29034     
29035     onPreviewLoad : function(file, image)
29036     {
29037         if(typeof(file.target) == 'undefined' || !file.target){
29038             return;
29039         }
29040         
29041         var width = image.dom.naturalWidth || image.dom.width;
29042         var height = image.dom.naturalHeight || image.dom.height;
29043         
29044         if(width > height){
29045             file.target.addClass('wide');
29046             return;
29047         }
29048         
29049         file.target.addClass('tall');
29050         return;
29051         
29052     },
29053     
29054     uploadFromSource : function(file, crop)
29055     {
29056         this.xhr = new XMLHttpRequest();
29057         
29058         this.managerEl.createChild({
29059             tag : 'div',
29060             cls : 'roo-document-manager-loading',
29061             cn : [
29062                 {
29063                     tag : 'div',
29064                     tooltip : file.name,
29065                     cls : 'roo-document-manager-thumb',
29066                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29067                 }
29068             ]
29069
29070         });
29071
29072         this.xhr.open(this.method, this.url, true);
29073         
29074         var headers = {
29075             "Accept": "application/json",
29076             "Cache-Control": "no-cache",
29077             "X-Requested-With": "XMLHttpRequest"
29078         };
29079         
29080         for (var headerName in headers) {
29081             var headerValue = headers[headerName];
29082             if (headerValue) {
29083                 this.xhr.setRequestHeader(headerName, headerValue);
29084             }
29085         }
29086         
29087         var _this = this;
29088         
29089         this.xhr.onload = function()
29090         {
29091             _this.xhrOnLoad(_this.xhr);
29092         }
29093         
29094         this.xhr.onerror = function()
29095         {
29096             _this.xhrOnError(_this.xhr);
29097         }
29098         
29099         var formData = new FormData();
29100
29101         formData.append('returnHTML', 'NO');
29102         
29103         formData.append('crop', crop);
29104         
29105         if(typeof(file.filename) != 'undefined'){
29106             formData.append('filename', file.filename);
29107         }
29108         
29109         if(typeof(file.mimetype) != 'undefined'){
29110             formData.append('mimetype', file.mimetype);
29111         }
29112         
29113         Roo.log(formData);
29114         
29115         if(this.fireEvent('prepare', this, formData) != false){
29116             this.xhr.send(formData);
29117         };
29118     }
29119 });
29120
29121 /*
29122 * Licence: LGPL
29123 */
29124
29125 /**
29126  * @class Roo.bootstrap.DocumentViewer
29127  * @extends Roo.bootstrap.Component
29128  * Bootstrap DocumentViewer class
29129  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29130  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29131  * 
29132  * @constructor
29133  * Create a new DocumentViewer
29134  * @param {Object} config The config object
29135  */
29136
29137 Roo.bootstrap.DocumentViewer = function(config){
29138     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29139     
29140     this.addEvents({
29141         /**
29142          * @event initial
29143          * Fire after initEvent
29144          * @param {Roo.bootstrap.DocumentViewer} this
29145          */
29146         "initial" : true,
29147         /**
29148          * @event click
29149          * Fire after click
29150          * @param {Roo.bootstrap.DocumentViewer} this
29151          */
29152         "click" : true,
29153         /**
29154          * @event download
29155          * Fire after download button
29156          * @param {Roo.bootstrap.DocumentViewer} this
29157          */
29158         "download" : true,
29159         /**
29160          * @event trash
29161          * Fire after trash button
29162          * @param {Roo.bootstrap.DocumentViewer} this
29163          */
29164         "trash" : true
29165         
29166     });
29167 };
29168
29169 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29170     
29171     showDownload : true,
29172     
29173     showTrash : true,
29174     
29175     getAutoCreate : function()
29176     {
29177         var cfg = {
29178             tag : 'div',
29179             cls : 'roo-document-viewer',
29180             cn : [
29181                 {
29182                     tag : 'div',
29183                     cls : 'roo-document-viewer-body',
29184                     cn : [
29185                         {
29186                             tag : 'div',
29187                             cls : 'roo-document-viewer-thumb',
29188                             cn : [
29189                                 {
29190                                     tag : 'img',
29191                                     cls : 'roo-document-viewer-image'
29192                                 }
29193                             ]
29194                         }
29195                     ]
29196                 },
29197                 {
29198                     tag : 'div',
29199                     cls : 'roo-document-viewer-footer',
29200                     cn : {
29201                         tag : 'div',
29202                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29203                         cn : [
29204                             {
29205                                 tag : 'div',
29206                                 cls : 'btn-group roo-document-viewer-download',
29207                                 cn : [
29208                                     {
29209                                         tag : 'button',
29210                                         cls : 'btn btn-default',
29211                                         html : '<i class="fa fa-download"></i>'
29212                                     }
29213                                 ]
29214                             },
29215                             {
29216                                 tag : 'div',
29217                                 cls : 'btn-group roo-document-viewer-trash',
29218                                 cn : [
29219                                     {
29220                                         tag : 'button',
29221                                         cls : 'btn btn-default',
29222                                         html : '<i class="fa fa-trash"></i>'
29223                                     }
29224                                 ]
29225                             }
29226                         ]
29227                     }
29228                 }
29229             ]
29230         };
29231         
29232         return cfg;
29233     },
29234     
29235     initEvents : function()
29236     {
29237         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29238         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29239         
29240         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29241         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29242         
29243         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29244         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29245         
29246         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29247         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29248         
29249         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29250         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29251         
29252         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29253         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29254         
29255         this.bodyEl.on('click', this.onClick, this);
29256         this.downloadBtn.on('click', this.onDownload, this);
29257         this.trashBtn.on('click', this.onTrash, this);
29258         
29259         this.downloadBtn.hide();
29260         this.trashBtn.hide();
29261         
29262         if(this.showDownload){
29263             this.downloadBtn.show();
29264         }
29265         
29266         if(this.showTrash){
29267             this.trashBtn.show();
29268         }
29269         
29270         if(!this.showDownload && !this.showTrash) {
29271             this.footerEl.hide();
29272         }
29273         
29274     },
29275     
29276     initial : function()
29277     {
29278         this.fireEvent('initial', this);
29279         
29280     },
29281     
29282     onClick : function(e)
29283     {
29284         e.preventDefault();
29285         
29286         this.fireEvent('click', this);
29287     },
29288     
29289     onDownload : function(e)
29290     {
29291         e.preventDefault();
29292         
29293         this.fireEvent('download', this);
29294     },
29295     
29296     onTrash : function(e)
29297     {
29298         e.preventDefault();
29299         
29300         this.fireEvent('trash', this);
29301     }
29302     
29303 });
29304 /*
29305  * - LGPL
29306  *
29307  * nav progress bar
29308  * 
29309  */
29310
29311 /**
29312  * @class Roo.bootstrap.NavProgressBar
29313  * @extends Roo.bootstrap.Component
29314  * Bootstrap NavProgressBar class
29315  * 
29316  * @constructor
29317  * Create a new nav progress bar
29318  * @param {Object} config The config object
29319  */
29320
29321 Roo.bootstrap.NavProgressBar = function(config){
29322     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29323
29324     this.bullets = this.bullets || [];
29325    
29326 //    Roo.bootstrap.NavProgressBar.register(this);
29327      this.addEvents({
29328         /**
29329              * @event changed
29330              * Fires when the active item changes
29331              * @param {Roo.bootstrap.NavProgressBar} this
29332              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29333              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29334          */
29335         'changed': true
29336      });
29337     
29338 };
29339
29340 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29341     
29342     bullets : [],
29343     barItems : [],
29344     
29345     getAutoCreate : function()
29346     {
29347         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29348         
29349         cfg = {
29350             tag : 'div',
29351             cls : 'roo-navigation-bar-group',
29352             cn : [
29353                 {
29354                     tag : 'div',
29355                     cls : 'roo-navigation-top-bar'
29356                 },
29357                 {
29358                     tag : 'div',
29359                     cls : 'roo-navigation-bullets-bar',
29360                     cn : [
29361                         {
29362                             tag : 'ul',
29363                             cls : 'roo-navigation-bar'
29364                         }
29365                     ]
29366                 },
29367                 
29368                 {
29369                     tag : 'div',
29370                     cls : 'roo-navigation-bottom-bar'
29371                 }
29372             ]
29373             
29374         };
29375         
29376         return cfg;
29377         
29378     },
29379     
29380     initEvents: function() 
29381     {
29382         
29383     },
29384     
29385     onRender : function(ct, position) 
29386     {
29387         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29388         
29389         if(this.bullets.length){
29390             Roo.each(this.bullets, function(b){
29391                this.addItem(b);
29392             }, this);
29393         }
29394         
29395         this.format();
29396         
29397     },
29398     
29399     addItem : function(cfg)
29400     {
29401         var item = new Roo.bootstrap.NavProgressItem(cfg);
29402         
29403         item.parentId = this.id;
29404         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29405         
29406         if(cfg.html){
29407             var top = new Roo.bootstrap.Element({
29408                 tag : 'div',
29409                 cls : 'roo-navigation-bar-text'
29410             });
29411             
29412             var bottom = new Roo.bootstrap.Element({
29413                 tag : 'div',
29414                 cls : 'roo-navigation-bar-text'
29415             });
29416             
29417             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29418             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29419             
29420             var topText = new Roo.bootstrap.Element({
29421                 tag : 'span',
29422                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29423             });
29424             
29425             var bottomText = new Roo.bootstrap.Element({
29426                 tag : 'span',
29427                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29428             });
29429             
29430             topText.onRender(top.el, null);
29431             bottomText.onRender(bottom.el, null);
29432             
29433             item.topEl = top;
29434             item.bottomEl = bottom;
29435         }
29436         
29437         this.barItems.push(item);
29438         
29439         return item;
29440     },
29441     
29442     getActive : function()
29443     {
29444         var active = false;
29445         
29446         Roo.each(this.barItems, function(v){
29447             
29448             if (!v.isActive()) {
29449                 return;
29450             }
29451             
29452             active = v;
29453             return false;
29454             
29455         });
29456         
29457         return active;
29458     },
29459     
29460     setActiveItem : function(item)
29461     {
29462         var prev = false;
29463         
29464         Roo.each(this.barItems, function(v){
29465             if (v.rid == item.rid) {
29466                 return ;
29467             }
29468             
29469             if (v.isActive()) {
29470                 v.setActive(false);
29471                 prev = v;
29472             }
29473         });
29474
29475         item.setActive(true);
29476         
29477         this.fireEvent('changed', this, item, prev);
29478     },
29479     
29480     getBarItem: function(rid)
29481     {
29482         var ret = false;
29483         
29484         Roo.each(this.barItems, function(e) {
29485             if (e.rid != rid) {
29486                 return;
29487             }
29488             
29489             ret =  e;
29490             return false;
29491         });
29492         
29493         return ret;
29494     },
29495     
29496     indexOfItem : function(item)
29497     {
29498         var index = false;
29499         
29500         Roo.each(this.barItems, function(v, i){
29501             
29502             if (v.rid != item.rid) {
29503                 return;
29504             }
29505             
29506             index = i;
29507             return false
29508         });
29509         
29510         return index;
29511     },
29512     
29513     setActiveNext : function()
29514     {
29515         var i = this.indexOfItem(this.getActive());
29516         
29517         if (i > this.barItems.length) {
29518             return;
29519         }
29520         
29521         this.setActiveItem(this.barItems[i+1]);
29522     },
29523     
29524     setActivePrev : function()
29525     {
29526         var i = this.indexOfItem(this.getActive());
29527         
29528         if (i  < 1) {
29529             return;
29530         }
29531         
29532         this.setActiveItem(this.barItems[i-1]);
29533     },
29534     
29535     format : function()
29536     {
29537         if(!this.barItems.length){
29538             return;
29539         }
29540      
29541         var width = 100 / this.barItems.length;
29542         
29543         Roo.each(this.barItems, function(i){
29544             i.el.setStyle('width', width + '%');
29545             i.topEl.el.setStyle('width', width + '%');
29546             i.bottomEl.el.setStyle('width', width + '%');
29547         }, this);
29548         
29549     }
29550     
29551 });
29552 /*
29553  * - LGPL
29554  *
29555  * Nav Progress Item
29556  * 
29557  */
29558
29559 /**
29560  * @class Roo.bootstrap.NavProgressItem
29561  * @extends Roo.bootstrap.Component
29562  * Bootstrap NavProgressItem class
29563  * @cfg {String} rid the reference id
29564  * @cfg {Boolean} active (true|false) Is item active default false
29565  * @cfg {Boolean} disabled (true|false) Is item active default false
29566  * @cfg {String} html
29567  * @cfg {String} position (top|bottom) text position default bottom
29568  * @cfg {String} icon show icon instead of number
29569  * 
29570  * @constructor
29571  * Create a new NavProgressItem
29572  * @param {Object} config The config object
29573  */
29574 Roo.bootstrap.NavProgressItem = function(config){
29575     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29576     this.addEvents({
29577         // raw events
29578         /**
29579          * @event click
29580          * The raw click event for the entire grid.
29581          * @param {Roo.bootstrap.NavProgressItem} this
29582          * @param {Roo.EventObject} e
29583          */
29584         "click" : true
29585     });
29586    
29587 };
29588
29589 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29590     
29591     rid : '',
29592     active : false,
29593     disabled : false,
29594     html : '',
29595     position : 'bottom',
29596     icon : false,
29597     
29598     getAutoCreate : function()
29599     {
29600         var iconCls = 'roo-navigation-bar-item-icon';
29601         
29602         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29603         
29604         var cfg = {
29605             tag: 'li',
29606             cls: 'roo-navigation-bar-item',
29607             cn : [
29608                 {
29609                     tag : 'i',
29610                     cls : iconCls
29611                 }
29612             ]
29613         };
29614         
29615         if(this.active){
29616             cfg.cls += ' active';
29617         }
29618         if(this.disabled){
29619             cfg.cls += ' disabled';
29620         }
29621         
29622         return cfg;
29623     },
29624     
29625     disable : function()
29626     {
29627         this.setDisabled(true);
29628     },
29629     
29630     enable : function()
29631     {
29632         this.setDisabled(false);
29633     },
29634     
29635     initEvents: function() 
29636     {
29637         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29638         
29639         this.iconEl.on('click', this.onClick, this);
29640     },
29641     
29642     onClick : function(e)
29643     {
29644         e.preventDefault();
29645         
29646         if(this.disabled){
29647             return;
29648         }
29649         
29650         if(this.fireEvent('click', this, e) === false){
29651             return;
29652         };
29653         
29654         this.parent().setActiveItem(this);
29655     },
29656     
29657     isActive: function () 
29658     {
29659         return this.active;
29660     },
29661     
29662     setActive : function(state)
29663     {
29664         if(this.active == state){
29665             return;
29666         }
29667         
29668         this.active = state;
29669         
29670         if (state) {
29671             this.el.addClass('active');
29672             return;
29673         }
29674         
29675         this.el.removeClass('active');
29676         
29677         return;
29678     },
29679     
29680     setDisabled : function(state)
29681     {
29682         if(this.disabled == state){
29683             return;
29684         }
29685         
29686         this.disabled = state;
29687         
29688         if (state) {
29689             this.el.addClass('disabled');
29690             return;
29691         }
29692         
29693         this.el.removeClass('disabled');
29694     },
29695     
29696     tooltipEl : function()
29697     {
29698         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29699     }
29700 });
29701  
29702
29703  /*
29704  * - LGPL
29705  *
29706  * FieldLabel
29707  * 
29708  */
29709
29710 /**
29711  * @class Roo.bootstrap.FieldLabel
29712  * @extends Roo.bootstrap.Component
29713  * Bootstrap FieldLabel class
29714  * @cfg {String} html contents of the element
29715  * @cfg {String} tag tag of the element default label
29716  * @cfg {String} cls class of the element
29717  * @cfg {String} target label target 
29718  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29719  * @cfg {String} invalidClass default "text-warning"
29720  * @cfg {String} validClass default "text-success"
29721  * @cfg {String} iconTooltip default "This field is required"
29722  * @cfg {String} indicatorpos (left|right) default left
29723  * 
29724  * @constructor
29725  * Create a new FieldLabel
29726  * @param {Object} config The config object
29727  */
29728
29729 Roo.bootstrap.FieldLabel = function(config){
29730     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29731     
29732     this.addEvents({
29733             /**
29734              * @event invalid
29735              * Fires after the field has been marked as invalid.
29736              * @param {Roo.form.FieldLabel} this
29737              * @param {String} msg The validation message
29738              */
29739             invalid : true,
29740             /**
29741              * @event valid
29742              * Fires after the field has been validated with no errors.
29743              * @param {Roo.form.FieldLabel} this
29744              */
29745             valid : true
29746         });
29747 };
29748
29749 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29750     
29751     tag: 'label',
29752     cls: '',
29753     html: '',
29754     target: '',
29755     allowBlank : true,
29756     invalidClass : 'has-warning',
29757     validClass : 'has-success',
29758     iconTooltip : 'This field is required',
29759     indicatorpos : 'left',
29760     
29761     getAutoCreate : function(){
29762         
29763         var cfg = {
29764             tag : this.tag,
29765             cls : 'roo-bootstrap-field-label ' + this.cls,
29766             for : this.target,
29767             cn : [
29768                 {
29769                     tag : 'i',
29770                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29771                     tooltip : this.iconTooltip
29772                 },
29773                 {
29774                     tag : 'span',
29775                     html : this.html
29776                 }
29777             ] 
29778         };
29779         
29780         if(this.indicatorpos == 'right'){
29781             var cfg = {
29782                 tag : this.tag,
29783                 cls : 'roo-bootstrap-field-label ' + this.cls,
29784                 for : this.target,
29785                 cn : [
29786                     {
29787                         tag : 'span',
29788                         html : this.html
29789                     },
29790                     {
29791                         tag : 'i',
29792                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29793                         tooltip : this.iconTooltip
29794                     }
29795                 ] 
29796             };
29797         }
29798         
29799         return cfg;
29800     },
29801     
29802     initEvents: function() 
29803     {
29804         Roo.bootstrap.Element.superclass.initEvents.call(this);
29805         
29806         this.indicator = this.indicatorEl();
29807         
29808         if(this.indicator){
29809             this.indicator.removeClass('visible');
29810             this.indicator.addClass('invisible');
29811         }
29812         
29813         Roo.bootstrap.FieldLabel.register(this);
29814     },
29815     
29816     indicatorEl : function()
29817     {
29818         var indicator = this.el.select('i.roo-required-indicator',true).first();
29819         
29820         if(!indicator){
29821             return false;
29822         }
29823         
29824         return indicator;
29825         
29826     },
29827     
29828     /**
29829      * Mark this field as valid
29830      */
29831     markValid : function()
29832     {
29833         if(this.indicator){
29834             this.indicator.removeClass('visible');
29835             this.indicator.addClass('invisible');
29836         }
29837         
29838         this.el.removeClass(this.invalidClass);
29839         
29840         this.el.addClass(this.validClass);
29841         
29842         this.fireEvent('valid', this);
29843     },
29844     
29845     /**
29846      * Mark this field as invalid
29847      * @param {String} msg The validation message
29848      */
29849     markInvalid : function(msg)
29850     {
29851         if(this.indicator){
29852             this.indicator.removeClass('invisible');
29853             this.indicator.addClass('visible');
29854         }
29855         
29856         this.el.removeClass(this.validClass);
29857         
29858         this.el.addClass(this.invalidClass);
29859         
29860         this.fireEvent('invalid', this, msg);
29861     }
29862     
29863    
29864 });
29865
29866 Roo.apply(Roo.bootstrap.FieldLabel, {
29867     
29868     groups: {},
29869     
29870      /**
29871     * register a FieldLabel Group
29872     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29873     */
29874     register : function(label)
29875     {
29876         if(this.groups.hasOwnProperty(label.target)){
29877             return;
29878         }
29879      
29880         this.groups[label.target] = label;
29881         
29882     },
29883     /**
29884     * fetch a FieldLabel Group based on the target
29885     * @param {string} target
29886     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29887     */
29888     get: function(target) {
29889         if (typeof(this.groups[target]) == 'undefined') {
29890             return false;
29891         }
29892         
29893         return this.groups[target] ;
29894     }
29895 });
29896
29897  
29898
29899  /*
29900  * - LGPL
29901  *
29902  * page DateSplitField.
29903  * 
29904  */
29905
29906
29907 /**
29908  * @class Roo.bootstrap.DateSplitField
29909  * @extends Roo.bootstrap.Component
29910  * Bootstrap DateSplitField class
29911  * @cfg {string} fieldLabel - the label associated
29912  * @cfg {Number} labelWidth set the width of label (0-12)
29913  * @cfg {String} labelAlign (top|left)
29914  * @cfg {Boolean} dayAllowBlank (true|false) default false
29915  * @cfg {Boolean} monthAllowBlank (true|false) default false
29916  * @cfg {Boolean} yearAllowBlank (true|false) default false
29917  * @cfg {string} dayPlaceholder 
29918  * @cfg {string} monthPlaceholder
29919  * @cfg {string} yearPlaceholder
29920  * @cfg {string} dayFormat default 'd'
29921  * @cfg {string} monthFormat default 'm'
29922  * @cfg {string} yearFormat default 'Y'
29923  * @cfg {Number} labellg set the width of label (1-12)
29924  * @cfg {Number} labelmd set the width of label (1-12)
29925  * @cfg {Number} labelsm set the width of label (1-12)
29926  * @cfg {Number} labelxs set the width of label (1-12)
29927
29928  *     
29929  * @constructor
29930  * Create a new DateSplitField
29931  * @param {Object} config The config object
29932  */
29933
29934 Roo.bootstrap.DateSplitField = function(config){
29935     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29936     
29937     this.addEvents({
29938         // raw events
29939          /**
29940          * @event years
29941          * getting the data of years
29942          * @param {Roo.bootstrap.DateSplitField} this
29943          * @param {Object} years
29944          */
29945         "years" : true,
29946         /**
29947          * @event days
29948          * getting the data of days
29949          * @param {Roo.bootstrap.DateSplitField} this
29950          * @param {Object} days
29951          */
29952         "days" : true,
29953         /**
29954          * @event invalid
29955          * Fires after the field has been marked as invalid.
29956          * @param {Roo.form.Field} this
29957          * @param {String} msg The validation message
29958          */
29959         invalid : true,
29960        /**
29961          * @event valid
29962          * Fires after the field has been validated with no errors.
29963          * @param {Roo.form.Field} this
29964          */
29965         valid : true
29966     });
29967 };
29968
29969 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29970     
29971     fieldLabel : '',
29972     labelAlign : 'top',
29973     labelWidth : 3,
29974     dayAllowBlank : false,
29975     monthAllowBlank : false,
29976     yearAllowBlank : false,
29977     dayPlaceholder : '',
29978     monthPlaceholder : '',
29979     yearPlaceholder : '',
29980     dayFormat : 'd',
29981     monthFormat : 'm',
29982     yearFormat : 'Y',
29983     isFormField : true,
29984     labellg : 0,
29985     labelmd : 0,
29986     labelsm : 0,
29987     labelxs : 0,
29988     
29989     getAutoCreate : function()
29990     {
29991         var cfg = {
29992             tag : 'div',
29993             cls : 'row roo-date-split-field-group',
29994             cn : [
29995                 {
29996                     tag : 'input',
29997                     type : 'hidden',
29998                     cls : 'form-hidden-field roo-date-split-field-group-value',
29999                     name : this.name
30000                 }
30001             ]
30002         };
30003         
30004         var labelCls = 'col-md-12';
30005         var contentCls = 'col-md-4';
30006         
30007         if(this.fieldLabel){
30008             
30009             var label = {
30010                 tag : 'div',
30011                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30012                 cn : [
30013                     {
30014                         tag : 'label',
30015                         html : this.fieldLabel
30016                     }
30017                 ]
30018             };
30019             
30020             if(this.labelAlign == 'left'){
30021             
30022                 if(this.labelWidth > 12){
30023                     label.style = "width: " + this.labelWidth + 'px';
30024                 }
30025
30026                 if(this.labelWidth < 13 && this.labelmd == 0){
30027                     this.labelmd = this.labelWidth;
30028                 }
30029
30030                 if(this.labellg > 0){
30031                     labelCls = ' col-lg-' + this.labellg;
30032                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30033                 }
30034
30035                 if(this.labelmd > 0){
30036                     labelCls = ' col-md-' + this.labelmd;
30037                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30038                 }
30039
30040                 if(this.labelsm > 0){
30041                     labelCls = ' col-sm-' + this.labelsm;
30042                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30043                 }
30044
30045                 if(this.labelxs > 0){
30046                     labelCls = ' col-xs-' + this.labelxs;
30047                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30048                 }
30049             }
30050             
30051             label.cls += ' ' + labelCls;
30052             
30053             cfg.cn.push(label);
30054         }
30055         
30056         Roo.each(['day', 'month', 'year'], function(t){
30057             cfg.cn.push({
30058                 tag : 'div',
30059                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30060             });
30061         }, this);
30062         
30063         return cfg;
30064     },
30065     
30066     inputEl: function ()
30067     {
30068         return this.el.select('.roo-date-split-field-group-value', true).first();
30069     },
30070     
30071     onRender : function(ct, position) 
30072     {
30073         var _this = this;
30074         
30075         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30076         
30077         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30078         
30079         this.dayField = new Roo.bootstrap.ComboBox({
30080             allowBlank : this.dayAllowBlank,
30081             alwaysQuery : true,
30082             displayField : 'value',
30083             editable : false,
30084             fieldLabel : '',
30085             forceSelection : true,
30086             mode : 'local',
30087             placeholder : this.dayPlaceholder,
30088             selectOnFocus : true,
30089             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30090             triggerAction : 'all',
30091             typeAhead : true,
30092             valueField : 'value',
30093             store : new Roo.data.SimpleStore({
30094                 data : (function() {    
30095                     var days = [];
30096                     _this.fireEvent('days', _this, days);
30097                     return days;
30098                 })(),
30099                 fields : [ 'value' ]
30100             }),
30101             listeners : {
30102                 select : function (_self, record, index)
30103                 {
30104                     _this.setValue(_this.getValue());
30105                 }
30106             }
30107         });
30108
30109         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30110         
30111         this.monthField = new Roo.bootstrap.MonthField({
30112             after : '<i class=\"fa fa-calendar\"></i>',
30113             allowBlank : this.monthAllowBlank,
30114             placeholder : this.monthPlaceholder,
30115             readOnly : true,
30116             listeners : {
30117                 render : function (_self)
30118                 {
30119                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30120                         e.preventDefault();
30121                         _self.focus();
30122                     });
30123                 },
30124                 select : function (_self, oldvalue, newvalue)
30125                 {
30126                     _this.setValue(_this.getValue());
30127                 }
30128             }
30129         });
30130         
30131         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30132         
30133         this.yearField = new Roo.bootstrap.ComboBox({
30134             allowBlank : this.yearAllowBlank,
30135             alwaysQuery : true,
30136             displayField : 'value',
30137             editable : false,
30138             fieldLabel : '',
30139             forceSelection : true,
30140             mode : 'local',
30141             placeholder : this.yearPlaceholder,
30142             selectOnFocus : true,
30143             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30144             triggerAction : 'all',
30145             typeAhead : true,
30146             valueField : 'value',
30147             store : new Roo.data.SimpleStore({
30148                 data : (function() {
30149                     var years = [];
30150                     _this.fireEvent('years', _this, years);
30151                     return years;
30152                 })(),
30153                 fields : [ 'value' ]
30154             }),
30155             listeners : {
30156                 select : function (_self, record, index)
30157                 {
30158                     _this.setValue(_this.getValue());
30159                 }
30160             }
30161         });
30162
30163         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30164     },
30165     
30166     setValue : function(v, format)
30167     {
30168         this.inputEl.dom.value = v;
30169         
30170         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30171         
30172         var d = Date.parseDate(v, f);
30173         
30174         if(!d){
30175             this.validate();
30176             return;
30177         }
30178         
30179         this.setDay(d.format(this.dayFormat));
30180         this.setMonth(d.format(this.monthFormat));
30181         this.setYear(d.format(this.yearFormat));
30182         
30183         this.validate();
30184         
30185         return;
30186     },
30187     
30188     setDay : function(v)
30189     {
30190         this.dayField.setValue(v);
30191         this.inputEl.dom.value = this.getValue();
30192         this.validate();
30193         return;
30194     },
30195     
30196     setMonth : function(v)
30197     {
30198         this.monthField.setValue(v, true);
30199         this.inputEl.dom.value = this.getValue();
30200         this.validate();
30201         return;
30202     },
30203     
30204     setYear : function(v)
30205     {
30206         this.yearField.setValue(v);
30207         this.inputEl.dom.value = this.getValue();
30208         this.validate();
30209         return;
30210     },
30211     
30212     getDay : function()
30213     {
30214         return this.dayField.getValue();
30215     },
30216     
30217     getMonth : function()
30218     {
30219         return this.monthField.getValue();
30220     },
30221     
30222     getYear : function()
30223     {
30224         return this.yearField.getValue();
30225     },
30226     
30227     getValue : function()
30228     {
30229         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30230         
30231         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30232         
30233         return date;
30234     },
30235     
30236     reset : function()
30237     {
30238         this.setDay('');
30239         this.setMonth('');
30240         this.setYear('');
30241         this.inputEl.dom.value = '';
30242         this.validate();
30243         return;
30244     },
30245     
30246     validate : function()
30247     {
30248         var d = this.dayField.validate();
30249         var m = this.monthField.validate();
30250         var y = this.yearField.validate();
30251         
30252         var valid = true;
30253         
30254         if(
30255                 (!this.dayAllowBlank && !d) ||
30256                 (!this.monthAllowBlank && !m) ||
30257                 (!this.yearAllowBlank && !y)
30258         ){
30259             valid = false;
30260         }
30261         
30262         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30263             return valid;
30264         }
30265         
30266         if(valid){
30267             this.markValid();
30268             return valid;
30269         }
30270         
30271         this.markInvalid();
30272         
30273         return valid;
30274     },
30275     
30276     markValid : function()
30277     {
30278         
30279         var label = this.el.select('label', true).first();
30280         var icon = this.el.select('i.fa-star', true).first();
30281
30282         if(label && icon){
30283             icon.remove();
30284         }
30285         
30286         this.fireEvent('valid', this);
30287     },
30288     
30289      /**
30290      * Mark this field as invalid
30291      * @param {String} msg The validation message
30292      */
30293     markInvalid : function(msg)
30294     {
30295         
30296         var label = this.el.select('label', true).first();
30297         var icon = this.el.select('i.fa-star', true).first();
30298
30299         if(label && !icon){
30300             this.el.select('.roo-date-split-field-label', true).createChild({
30301                 tag : 'i',
30302                 cls : 'text-danger fa fa-lg fa-star',
30303                 tooltip : 'This field is required',
30304                 style : 'margin-right:5px;'
30305             }, label, true);
30306         }
30307         
30308         this.fireEvent('invalid', this, msg);
30309     },
30310     
30311     clearInvalid : function()
30312     {
30313         var label = this.el.select('label', true).first();
30314         var icon = this.el.select('i.fa-star', true).first();
30315
30316         if(label && icon){
30317             icon.remove();
30318         }
30319         
30320         this.fireEvent('valid', this);
30321     },
30322     
30323     getName: function()
30324     {
30325         return this.name;
30326     }
30327     
30328 });
30329
30330  /**
30331  *
30332  * This is based on 
30333  * http://masonry.desandro.com
30334  *
30335  * The idea is to render all the bricks based on vertical width...
30336  *
30337  * The original code extends 'outlayer' - we might need to use that....
30338  * 
30339  */
30340
30341
30342 /**
30343  * @class Roo.bootstrap.LayoutMasonry
30344  * @extends Roo.bootstrap.Component
30345  * Bootstrap Layout Masonry class
30346  * 
30347  * @constructor
30348  * Create a new Element
30349  * @param {Object} config The config object
30350  */
30351
30352 Roo.bootstrap.LayoutMasonry = function(config){
30353     
30354     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30355     
30356     this.bricks = [];
30357     
30358     Roo.bootstrap.LayoutMasonry.register(this);
30359     
30360     this.addEvents({
30361         // raw events
30362         /**
30363          * @event layout
30364          * Fire after layout the items
30365          * @param {Roo.bootstrap.LayoutMasonry} this
30366          * @param {Roo.EventObject} e
30367          */
30368         "layout" : true
30369     });
30370     
30371 };
30372
30373 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30374     
30375     /**
30376      * @cfg {Boolean} isLayoutInstant = no animation?
30377      */   
30378     isLayoutInstant : false, // needed?
30379    
30380     /**
30381      * @cfg {Number} boxWidth  width of the columns
30382      */   
30383     boxWidth : 450,
30384     
30385       /**
30386      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30387      */   
30388     boxHeight : 0,
30389     
30390     /**
30391      * @cfg {Number} padWidth padding below box..
30392      */   
30393     padWidth : 10, 
30394     
30395     /**
30396      * @cfg {Number} gutter gutter width..
30397      */   
30398     gutter : 10,
30399     
30400      /**
30401      * @cfg {Number} maxCols maximum number of columns
30402      */   
30403     
30404     maxCols: 0,
30405     
30406     /**
30407      * @cfg {Boolean} isAutoInitial defalut true
30408      */   
30409     isAutoInitial : true, 
30410     
30411     containerWidth: 0,
30412     
30413     /**
30414      * @cfg {Boolean} isHorizontal defalut false
30415      */   
30416     isHorizontal : false, 
30417
30418     currentSize : null,
30419     
30420     tag: 'div',
30421     
30422     cls: '',
30423     
30424     bricks: null, //CompositeElement
30425     
30426     cols : 1,
30427     
30428     _isLayoutInited : false,
30429     
30430 //    isAlternative : false, // only use for vertical layout...
30431     
30432     /**
30433      * @cfg {Number} alternativePadWidth padding below box..
30434      */   
30435     alternativePadWidth : 50,
30436     
30437     selectedBrick : [],
30438     
30439     getAutoCreate : function(){
30440         
30441         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30442         
30443         var cfg = {
30444             tag: this.tag,
30445             cls: 'blog-masonary-wrapper ' + this.cls,
30446             cn : {
30447                 cls : 'mas-boxes masonary'
30448             }
30449         };
30450         
30451         return cfg;
30452     },
30453     
30454     getChildContainer: function( )
30455     {
30456         if (this.boxesEl) {
30457             return this.boxesEl;
30458         }
30459         
30460         this.boxesEl = this.el.select('.mas-boxes').first();
30461         
30462         return this.boxesEl;
30463     },
30464     
30465     
30466     initEvents : function()
30467     {
30468         var _this = this;
30469         
30470         if(this.isAutoInitial){
30471             Roo.log('hook children rendered');
30472             this.on('childrenrendered', function() {
30473                 Roo.log('children rendered');
30474                 _this.initial();
30475             } ,this);
30476         }
30477     },
30478     
30479     initial : function()
30480     {
30481         this.selectedBrick = [];
30482         
30483         this.currentSize = this.el.getBox(true);
30484         
30485         Roo.EventManager.onWindowResize(this.resize, this); 
30486
30487         if(!this.isAutoInitial){
30488             this.layout();
30489             return;
30490         }
30491         
30492         this.layout();
30493         
30494         return;
30495         //this.layout.defer(500,this);
30496         
30497     },
30498     
30499     resize : function()
30500     {
30501         var cs = this.el.getBox(true);
30502         
30503         if (
30504                 this.currentSize.width == cs.width && 
30505                 this.currentSize.x == cs.x && 
30506                 this.currentSize.height == cs.height && 
30507                 this.currentSize.y == cs.y 
30508         ) {
30509             Roo.log("no change in with or X or Y");
30510             return;
30511         }
30512         
30513         this.currentSize = cs;
30514         
30515         this.layout();
30516         
30517     },
30518     
30519     layout : function()
30520     {   
30521         this._resetLayout();
30522         
30523         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30524         
30525         this.layoutItems( isInstant );
30526       
30527         this._isLayoutInited = true;
30528         
30529         this.fireEvent('layout', this);
30530         
30531     },
30532     
30533     _resetLayout : function()
30534     {
30535         if(this.isHorizontal){
30536             this.horizontalMeasureColumns();
30537             return;
30538         }
30539         
30540         this.verticalMeasureColumns();
30541         
30542     },
30543     
30544     verticalMeasureColumns : function()
30545     {
30546         this.getContainerWidth();
30547         
30548 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30549 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30550 //            return;
30551 //        }
30552         
30553         var boxWidth = this.boxWidth + this.padWidth;
30554         
30555         if(this.containerWidth < this.boxWidth){
30556             boxWidth = this.containerWidth
30557         }
30558         
30559         var containerWidth = this.containerWidth;
30560         
30561         var cols = Math.floor(containerWidth / boxWidth);
30562         
30563         this.cols = Math.max( cols, 1 );
30564         
30565         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30566         
30567         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30568         
30569         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30570         
30571         this.colWidth = boxWidth + avail - this.padWidth;
30572         
30573         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30574         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30575     },
30576     
30577     horizontalMeasureColumns : function()
30578     {
30579         this.getContainerWidth();
30580         
30581         var boxWidth = this.boxWidth;
30582         
30583         if(this.containerWidth < boxWidth){
30584             boxWidth = this.containerWidth;
30585         }
30586         
30587         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30588         
30589         this.el.setHeight(boxWidth);
30590         
30591     },
30592     
30593     getContainerWidth : function()
30594     {
30595         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30596     },
30597     
30598     layoutItems : function( isInstant )
30599     {
30600         Roo.log(this.bricks);
30601         
30602         var items = Roo.apply([], this.bricks);
30603         
30604         if(this.isHorizontal){
30605             this._horizontalLayoutItems( items , isInstant );
30606             return;
30607         }
30608         
30609 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30610 //            this._verticalAlternativeLayoutItems( items , isInstant );
30611 //            return;
30612 //        }
30613         
30614         this._verticalLayoutItems( items , isInstant );
30615         
30616     },
30617     
30618     _verticalLayoutItems : function ( items , isInstant)
30619     {
30620         if ( !items || !items.length ) {
30621             return;
30622         }
30623         
30624         var standard = [
30625             ['xs', 'xs', 'xs', 'tall'],
30626             ['xs', 'xs', 'tall'],
30627             ['xs', 'xs', 'sm'],
30628             ['xs', 'xs', 'xs'],
30629             ['xs', 'tall'],
30630             ['xs', 'sm'],
30631             ['xs', 'xs'],
30632             ['xs'],
30633             
30634             ['sm', 'xs', 'xs'],
30635             ['sm', 'xs'],
30636             ['sm'],
30637             
30638             ['tall', 'xs', 'xs', 'xs'],
30639             ['tall', 'xs', 'xs'],
30640             ['tall', 'xs'],
30641             ['tall']
30642             
30643         ];
30644         
30645         var queue = [];
30646         
30647         var boxes = [];
30648         
30649         var box = [];
30650         
30651         Roo.each(items, function(item, k){
30652             
30653             switch (item.size) {
30654                 // these layouts take up a full box,
30655                 case 'md' :
30656                 case 'md-left' :
30657                 case 'md-right' :
30658                 case 'wide' :
30659                     
30660                     if(box.length){
30661                         boxes.push(box);
30662                         box = [];
30663                     }
30664                     
30665                     boxes.push([item]);
30666                     
30667                     break;
30668                     
30669                 case 'xs' :
30670                 case 'sm' :
30671                 case 'tall' :
30672                     
30673                     box.push(item);
30674                     
30675                     break;
30676                 default :
30677                     break;
30678                     
30679             }
30680             
30681         }, this);
30682         
30683         if(box.length){
30684             boxes.push(box);
30685             box = [];
30686         }
30687         
30688         var filterPattern = function(box, length)
30689         {
30690             if(!box.length){
30691                 return;
30692             }
30693             
30694             var match = false;
30695             
30696             var pattern = box.slice(0, length);
30697             
30698             var format = [];
30699             
30700             Roo.each(pattern, function(i){
30701                 format.push(i.size);
30702             }, this);
30703             
30704             Roo.each(standard, function(s){
30705                 
30706                 if(String(s) != String(format)){
30707                     return;
30708                 }
30709                 
30710                 match = true;
30711                 return false;
30712                 
30713             }, this);
30714             
30715             if(!match && length == 1){
30716                 return;
30717             }
30718             
30719             if(!match){
30720                 filterPattern(box, length - 1);
30721                 return;
30722             }
30723                 
30724             queue.push(pattern);
30725
30726             box = box.slice(length, box.length);
30727
30728             filterPattern(box, 4);
30729
30730             return;
30731             
30732         }
30733         
30734         Roo.each(boxes, function(box, k){
30735             
30736             if(!box.length){
30737                 return;
30738             }
30739             
30740             if(box.length == 1){
30741                 queue.push(box);
30742                 return;
30743             }
30744             
30745             filterPattern(box, 4);
30746             
30747         }, this);
30748         
30749         this._processVerticalLayoutQueue( queue, isInstant );
30750         
30751     },
30752     
30753 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30754 //    {
30755 //        if ( !items || !items.length ) {
30756 //            return;
30757 //        }
30758 //
30759 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30760 //        
30761 //    },
30762     
30763     _horizontalLayoutItems : function ( items , isInstant)
30764     {
30765         if ( !items || !items.length || items.length < 3) {
30766             return;
30767         }
30768         
30769         items.reverse();
30770         
30771         var eItems = items.slice(0, 3);
30772         
30773         items = items.slice(3, items.length);
30774         
30775         var standard = [
30776             ['xs', 'xs', 'xs', 'wide'],
30777             ['xs', 'xs', 'wide'],
30778             ['xs', 'xs', 'sm'],
30779             ['xs', 'xs', 'xs'],
30780             ['xs', 'wide'],
30781             ['xs', 'sm'],
30782             ['xs', 'xs'],
30783             ['xs'],
30784             
30785             ['sm', 'xs', 'xs'],
30786             ['sm', 'xs'],
30787             ['sm'],
30788             
30789             ['wide', 'xs', 'xs', 'xs'],
30790             ['wide', 'xs', 'xs'],
30791             ['wide', 'xs'],
30792             ['wide'],
30793             
30794             ['wide-thin']
30795         ];
30796         
30797         var queue = [];
30798         
30799         var boxes = [];
30800         
30801         var box = [];
30802         
30803         Roo.each(items, function(item, k){
30804             
30805             switch (item.size) {
30806                 case 'md' :
30807                 case 'md-left' :
30808                 case 'md-right' :
30809                 case 'tall' :
30810                     
30811                     if(box.length){
30812                         boxes.push(box);
30813                         box = [];
30814                     }
30815                     
30816                     boxes.push([item]);
30817                     
30818                     break;
30819                     
30820                 case 'xs' :
30821                 case 'sm' :
30822                 case 'wide' :
30823                 case 'wide-thin' :
30824                     
30825                     box.push(item);
30826                     
30827                     break;
30828                 default :
30829                     break;
30830                     
30831             }
30832             
30833         }, this);
30834         
30835         if(box.length){
30836             boxes.push(box);
30837             box = [];
30838         }
30839         
30840         var filterPattern = function(box, length)
30841         {
30842             if(!box.length){
30843                 return;
30844             }
30845             
30846             var match = false;
30847             
30848             var pattern = box.slice(0, length);
30849             
30850             var format = [];
30851             
30852             Roo.each(pattern, function(i){
30853                 format.push(i.size);
30854             }, this);
30855             
30856             Roo.each(standard, function(s){
30857                 
30858                 if(String(s) != String(format)){
30859                     return;
30860                 }
30861                 
30862                 match = true;
30863                 return false;
30864                 
30865             }, this);
30866             
30867             if(!match && length == 1){
30868                 return;
30869             }
30870             
30871             if(!match){
30872                 filterPattern(box, length - 1);
30873                 return;
30874             }
30875                 
30876             queue.push(pattern);
30877
30878             box = box.slice(length, box.length);
30879
30880             filterPattern(box, 4);
30881
30882             return;
30883             
30884         }
30885         
30886         Roo.each(boxes, function(box, k){
30887             
30888             if(!box.length){
30889                 return;
30890             }
30891             
30892             if(box.length == 1){
30893                 queue.push(box);
30894                 return;
30895             }
30896             
30897             filterPattern(box, 4);
30898             
30899         }, this);
30900         
30901         
30902         var prune = [];
30903         
30904         var pos = this.el.getBox(true);
30905         
30906         var minX = pos.x;
30907         
30908         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30909         
30910         var hit_end = false;
30911         
30912         Roo.each(queue, function(box){
30913             
30914             if(hit_end){
30915                 
30916                 Roo.each(box, function(b){
30917                 
30918                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30919                     b.el.hide();
30920
30921                 }, this);
30922
30923                 return;
30924             }
30925             
30926             var mx = 0;
30927             
30928             Roo.each(box, function(b){
30929                 
30930                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30931                 b.el.show();
30932
30933                 mx = Math.max(mx, b.x);
30934                 
30935             }, this);
30936             
30937             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30938             
30939             if(maxX < minX){
30940                 
30941                 Roo.each(box, function(b){
30942                 
30943                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30944                     b.el.hide();
30945                     
30946                 }, this);
30947                 
30948                 hit_end = true;
30949                 
30950                 return;
30951             }
30952             
30953             prune.push(box);
30954             
30955         }, this);
30956         
30957         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30958     },
30959     
30960     /** Sets position of item in DOM
30961     * @param {Element} item
30962     * @param {Number} x - horizontal position
30963     * @param {Number} y - vertical position
30964     * @param {Boolean} isInstant - disables transitions
30965     */
30966     _processVerticalLayoutQueue : function( queue, isInstant )
30967     {
30968         var pos = this.el.getBox(true);
30969         var x = pos.x;
30970         var y = pos.y;
30971         var maxY = [];
30972         
30973         for (var i = 0; i < this.cols; i++){
30974             maxY[i] = pos.y;
30975         }
30976         
30977         Roo.each(queue, function(box, k){
30978             
30979             var col = k % this.cols;
30980             
30981             Roo.each(box, function(b,kk){
30982                 
30983                 b.el.position('absolute');
30984                 
30985                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30986                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30987                 
30988                 if(b.size == 'md-left' || b.size == 'md-right'){
30989                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30990                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30991                 }
30992                 
30993                 b.el.setWidth(width);
30994                 b.el.setHeight(height);
30995                 // iframe?
30996                 b.el.select('iframe',true).setSize(width,height);
30997                 
30998             }, this);
30999             
31000             for (var i = 0; i < this.cols; i++){
31001                 
31002                 if(maxY[i] < maxY[col]){
31003                     col = i;
31004                     continue;
31005                 }
31006                 
31007                 col = Math.min(col, i);
31008                 
31009             }
31010             
31011             x = pos.x + col * (this.colWidth + this.padWidth);
31012             
31013             y = maxY[col];
31014             
31015             var positions = [];
31016             
31017             switch (box.length){
31018                 case 1 :
31019                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31020                     break;
31021                 case 2 :
31022                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31023                     break;
31024                 case 3 :
31025                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31026                     break;
31027                 case 4 :
31028                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31029                     break;
31030                 default :
31031                     break;
31032             }
31033             
31034             Roo.each(box, function(b,kk){
31035                 
31036                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31037                 
31038                 var sz = b.el.getSize();
31039                 
31040                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31041                 
31042             }, this);
31043             
31044         }, this);
31045         
31046         var mY = 0;
31047         
31048         for (var i = 0; i < this.cols; i++){
31049             mY = Math.max(mY, maxY[i]);
31050         }
31051         
31052         this.el.setHeight(mY - pos.y);
31053         
31054     },
31055     
31056 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31057 //    {
31058 //        var pos = this.el.getBox(true);
31059 //        var x = pos.x;
31060 //        var y = pos.y;
31061 //        var maxX = pos.right;
31062 //        
31063 //        var maxHeight = 0;
31064 //        
31065 //        Roo.each(items, function(item, k){
31066 //            
31067 //            var c = k % 2;
31068 //            
31069 //            item.el.position('absolute');
31070 //                
31071 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31072 //
31073 //            item.el.setWidth(width);
31074 //
31075 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31076 //
31077 //            item.el.setHeight(height);
31078 //            
31079 //            if(c == 0){
31080 //                item.el.setXY([x, y], isInstant ? false : true);
31081 //            } else {
31082 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31083 //            }
31084 //            
31085 //            y = y + height + this.alternativePadWidth;
31086 //            
31087 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31088 //            
31089 //        }, this);
31090 //        
31091 //        this.el.setHeight(maxHeight);
31092 //        
31093 //    },
31094     
31095     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31096     {
31097         var pos = this.el.getBox(true);
31098         
31099         var minX = pos.x;
31100         var minY = pos.y;
31101         
31102         var maxX = pos.right;
31103         
31104         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31105         
31106         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31107         
31108         Roo.each(queue, function(box, k){
31109             
31110             Roo.each(box, function(b, kk){
31111                 
31112                 b.el.position('absolute');
31113                 
31114                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31115                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31116                 
31117                 if(b.size == 'md-left' || b.size == 'md-right'){
31118                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31119                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31120                 }
31121                 
31122                 b.el.setWidth(width);
31123                 b.el.setHeight(height);
31124                 
31125             }, this);
31126             
31127             if(!box.length){
31128                 return;
31129             }
31130             
31131             var positions = [];
31132             
31133             switch (box.length){
31134                 case 1 :
31135                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31136                     break;
31137                 case 2 :
31138                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31139                     break;
31140                 case 3 :
31141                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31142                     break;
31143                 case 4 :
31144                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31145                     break;
31146                 default :
31147                     break;
31148             }
31149             
31150             Roo.each(box, function(b,kk){
31151                 
31152                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31153                 
31154                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31155                 
31156             }, this);
31157             
31158         }, this);
31159         
31160     },
31161     
31162     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31163     {
31164         Roo.each(eItems, function(b,k){
31165             
31166             b.size = (k == 0) ? 'sm' : 'xs';
31167             b.x = (k == 0) ? 2 : 1;
31168             b.y = (k == 0) ? 2 : 1;
31169             
31170             b.el.position('absolute');
31171             
31172             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31173                 
31174             b.el.setWidth(width);
31175             
31176             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31177             
31178             b.el.setHeight(height);
31179             
31180         }, this);
31181
31182         var positions = [];
31183         
31184         positions.push({
31185             x : maxX - this.unitWidth * 2 - this.gutter,
31186             y : minY
31187         });
31188         
31189         positions.push({
31190             x : maxX - this.unitWidth,
31191             y : minY + (this.unitWidth + this.gutter) * 2
31192         });
31193         
31194         positions.push({
31195             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31196             y : minY
31197         });
31198         
31199         Roo.each(eItems, function(b,k){
31200             
31201             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31202
31203         }, this);
31204         
31205     },
31206     
31207     getVerticalOneBoxColPositions : function(x, y, box)
31208     {
31209         var pos = [];
31210         
31211         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31212         
31213         if(box[0].size == 'md-left'){
31214             rand = 0;
31215         }
31216         
31217         if(box[0].size == 'md-right'){
31218             rand = 1;
31219         }
31220         
31221         pos.push({
31222             x : x + (this.unitWidth + this.gutter) * rand,
31223             y : y
31224         });
31225         
31226         return pos;
31227     },
31228     
31229     getVerticalTwoBoxColPositions : function(x, y, box)
31230     {
31231         var pos = [];
31232         
31233         if(box[0].size == 'xs'){
31234             
31235             pos.push({
31236                 x : x,
31237                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31238             });
31239
31240             pos.push({
31241                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31242                 y : y
31243             });
31244             
31245             return pos;
31246             
31247         }
31248         
31249         pos.push({
31250             x : x,
31251             y : y
31252         });
31253
31254         pos.push({
31255             x : x + (this.unitWidth + this.gutter) * 2,
31256             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31257         });
31258         
31259         return pos;
31260         
31261     },
31262     
31263     getVerticalThreeBoxColPositions : function(x, y, box)
31264     {
31265         var pos = [];
31266         
31267         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31268             
31269             pos.push({
31270                 x : x,
31271                 y : y
31272             });
31273
31274             pos.push({
31275                 x : x + (this.unitWidth + this.gutter) * 1,
31276                 y : y
31277             });
31278             
31279             pos.push({
31280                 x : x + (this.unitWidth + this.gutter) * 2,
31281                 y : y
31282             });
31283             
31284             return pos;
31285             
31286         }
31287         
31288         if(box[0].size == 'xs' && box[1].size == 'xs'){
31289             
31290             pos.push({
31291                 x : x,
31292                 y : y
31293             });
31294
31295             pos.push({
31296                 x : x,
31297                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31298             });
31299             
31300             pos.push({
31301                 x : x + (this.unitWidth + this.gutter) * 1,
31302                 y : y
31303             });
31304             
31305             return pos;
31306             
31307         }
31308         
31309         pos.push({
31310             x : x,
31311             y : y
31312         });
31313
31314         pos.push({
31315             x : x + (this.unitWidth + this.gutter) * 2,
31316             y : y
31317         });
31318
31319         pos.push({
31320             x : x + (this.unitWidth + this.gutter) * 2,
31321             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31322         });
31323             
31324         return pos;
31325         
31326     },
31327     
31328     getVerticalFourBoxColPositions : function(x, y, box)
31329     {
31330         var pos = [];
31331         
31332         if(box[0].size == 'xs'){
31333             
31334             pos.push({
31335                 x : x,
31336                 y : y
31337             });
31338
31339             pos.push({
31340                 x : x,
31341                 y : y + (this.unitHeight + this.gutter) * 1
31342             });
31343             
31344             pos.push({
31345                 x : x,
31346                 y : y + (this.unitHeight + this.gutter) * 2
31347             });
31348             
31349             pos.push({
31350                 x : x + (this.unitWidth + this.gutter) * 1,
31351                 y : y
31352             });
31353             
31354             return pos;
31355             
31356         }
31357         
31358         pos.push({
31359             x : x,
31360             y : y
31361         });
31362
31363         pos.push({
31364             x : x + (this.unitWidth + this.gutter) * 2,
31365             y : y
31366         });
31367
31368         pos.push({
31369             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31370             y : y + (this.unitHeight + this.gutter) * 1
31371         });
31372
31373         pos.push({
31374             x : x + (this.unitWidth + this.gutter) * 2,
31375             y : y + (this.unitWidth + this.gutter) * 2
31376         });
31377
31378         return pos;
31379         
31380     },
31381     
31382     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31383     {
31384         var pos = [];
31385         
31386         if(box[0].size == 'md-left'){
31387             pos.push({
31388                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31389                 y : minY
31390             });
31391             
31392             return pos;
31393         }
31394         
31395         if(box[0].size == 'md-right'){
31396             pos.push({
31397                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31398                 y : minY + (this.unitWidth + this.gutter) * 1
31399             });
31400             
31401             return pos;
31402         }
31403         
31404         var rand = Math.floor(Math.random() * (4 - box[0].y));
31405         
31406         pos.push({
31407             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31408             y : minY + (this.unitWidth + this.gutter) * rand
31409         });
31410         
31411         return pos;
31412         
31413     },
31414     
31415     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31416     {
31417         var pos = [];
31418         
31419         if(box[0].size == 'xs'){
31420             
31421             pos.push({
31422                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31423                 y : minY
31424             });
31425
31426             pos.push({
31427                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31428                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31429             });
31430             
31431             return pos;
31432             
31433         }
31434         
31435         pos.push({
31436             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31437             y : minY
31438         });
31439
31440         pos.push({
31441             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31442             y : minY + (this.unitWidth + this.gutter) * 2
31443         });
31444         
31445         return pos;
31446         
31447     },
31448     
31449     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31450     {
31451         var pos = [];
31452         
31453         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31454             
31455             pos.push({
31456                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31457                 y : minY
31458             });
31459
31460             pos.push({
31461                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31462                 y : minY + (this.unitWidth + this.gutter) * 1
31463             });
31464             
31465             pos.push({
31466                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31467                 y : minY + (this.unitWidth + this.gutter) * 2
31468             });
31469             
31470             return pos;
31471             
31472         }
31473         
31474         if(box[0].size == 'xs' && box[1].size == 'xs'){
31475             
31476             pos.push({
31477                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31478                 y : minY
31479             });
31480
31481             pos.push({
31482                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31483                 y : minY
31484             });
31485             
31486             pos.push({
31487                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31488                 y : minY + (this.unitWidth + this.gutter) * 1
31489             });
31490             
31491             return pos;
31492             
31493         }
31494         
31495         pos.push({
31496             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31497             y : minY
31498         });
31499
31500         pos.push({
31501             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31502             y : minY + (this.unitWidth + this.gutter) * 2
31503         });
31504
31505         pos.push({
31506             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31507             y : minY + (this.unitWidth + this.gutter) * 2
31508         });
31509             
31510         return pos;
31511         
31512     },
31513     
31514     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31515     {
31516         var pos = [];
31517         
31518         if(box[0].size == 'xs'){
31519             
31520             pos.push({
31521                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31522                 y : minY
31523             });
31524
31525             pos.push({
31526                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31527                 y : minY
31528             });
31529             
31530             pos.push({
31531                 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),
31532                 y : minY
31533             });
31534             
31535             pos.push({
31536                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31537                 y : minY + (this.unitWidth + this.gutter) * 1
31538             });
31539             
31540             return pos;
31541             
31542         }
31543         
31544         pos.push({
31545             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31546             y : minY
31547         });
31548         
31549         pos.push({
31550             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31551             y : minY + (this.unitWidth + this.gutter) * 2
31552         });
31553         
31554         pos.push({
31555             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31556             y : minY + (this.unitWidth + this.gutter) * 2
31557         });
31558         
31559         pos.push({
31560             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),
31561             y : minY + (this.unitWidth + this.gutter) * 2
31562         });
31563
31564         return pos;
31565         
31566     },
31567     
31568     /**
31569     * remove a Masonry Brick
31570     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31571     */
31572     removeBrick : function(brick_id)
31573     {
31574         if (!brick_id) {
31575             return;
31576         }
31577         
31578         for (var i = 0; i<this.bricks.length; i++) {
31579             if (this.bricks[i].id == brick_id) {
31580                 this.bricks.splice(i,1);
31581                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31582                 this.initial();
31583             }
31584         }
31585     },
31586     
31587     /**
31588     * adds a Masonry Brick
31589     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31590     */
31591     addBrick : function(cfg)
31592     {
31593         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31594         //this.register(cn);
31595         cn.parentId = this.id;
31596         cn.onRender(this.el, null);
31597         return cn;
31598     },
31599     
31600     /**
31601     * register a Masonry Brick
31602     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31603     */
31604     
31605     register : function(brick)
31606     {
31607         this.bricks.push(brick);
31608         brick.masonryId = this.id;
31609     },
31610     
31611     /**
31612     * clear all the Masonry Brick
31613     */
31614     clearAll : function()
31615     {
31616         this.bricks = [];
31617         //this.getChildContainer().dom.innerHTML = "";
31618         this.el.dom.innerHTML = '';
31619     },
31620     
31621     getSelected : function()
31622     {
31623         if (!this.selectedBrick) {
31624             return false;
31625         }
31626         
31627         return this.selectedBrick;
31628     }
31629 });
31630
31631 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31632     
31633     groups: {},
31634      /**
31635     * register a Masonry Layout
31636     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31637     */
31638     
31639     register : function(layout)
31640     {
31641         this.groups[layout.id] = layout;
31642     },
31643     /**
31644     * fetch a  Masonry Layout based on the masonry layout ID
31645     * @param {string} the masonry layout to add
31646     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31647     */
31648     
31649     get: function(layout_id) {
31650         if (typeof(this.groups[layout_id]) == 'undefined') {
31651             return false;
31652         }
31653         return this.groups[layout_id] ;
31654     }
31655     
31656     
31657     
31658 });
31659
31660  
31661
31662  /**
31663  *
31664  * This is based on 
31665  * http://masonry.desandro.com
31666  *
31667  * The idea is to render all the bricks based on vertical width...
31668  *
31669  * The original code extends 'outlayer' - we might need to use that....
31670  * 
31671  */
31672
31673
31674 /**
31675  * @class Roo.bootstrap.LayoutMasonryAuto
31676  * @extends Roo.bootstrap.Component
31677  * Bootstrap Layout Masonry class
31678  * 
31679  * @constructor
31680  * Create a new Element
31681  * @param {Object} config The config object
31682  */
31683
31684 Roo.bootstrap.LayoutMasonryAuto = function(config){
31685     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31686 };
31687
31688 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31689     
31690       /**
31691      * @cfg {Boolean} isFitWidth  - resize the width..
31692      */   
31693     isFitWidth : false,  // options..
31694     /**
31695      * @cfg {Boolean} isOriginLeft = left align?
31696      */   
31697     isOriginLeft : true,
31698     /**
31699      * @cfg {Boolean} isOriginTop = top align?
31700      */   
31701     isOriginTop : false,
31702     /**
31703      * @cfg {Boolean} isLayoutInstant = no animation?
31704      */   
31705     isLayoutInstant : false, // needed?
31706     /**
31707      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31708      */   
31709     isResizingContainer : true,
31710     /**
31711      * @cfg {Number} columnWidth  width of the columns 
31712      */   
31713     
31714     columnWidth : 0,
31715     
31716     /**
31717      * @cfg {Number} maxCols maximum number of columns
31718      */   
31719     
31720     maxCols: 0,
31721     /**
31722      * @cfg {Number} padHeight padding below box..
31723      */   
31724     
31725     padHeight : 10, 
31726     
31727     /**
31728      * @cfg {Boolean} isAutoInitial defalut true
31729      */   
31730     
31731     isAutoInitial : true, 
31732     
31733     // private?
31734     gutter : 0,
31735     
31736     containerWidth: 0,
31737     initialColumnWidth : 0,
31738     currentSize : null,
31739     
31740     colYs : null, // array.
31741     maxY : 0,
31742     padWidth: 10,
31743     
31744     
31745     tag: 'div',
31746     cls: '',
31747     bricks: null, //CompositeElement
31748     cols : 0, // array?
31749     // element : null, // wrapped now this.el
31750     _isLayoutInited : null, 
31751     
31752     
31753     getAutoCreate : function(){
31754         
31755         var cfg = {
31756             tag: this.tag,
31757             cls: 'blog-masonary-wrapper ' + this.cls,
31758             cn : {
31759                 cls : 'mas-boxes masonary'
31760             }
31761         };
31762         
31763         return cfg;
31764     },
31765     
31766     getChildContainer: function( )
31767     {
31768         if (this.boxesEl) {
31769             return this.boxesEl;
31770         }
31771         
31772         this.boxesEl = this.el.select('.mas-boxes').first();
31773         
31774         return this.boxesEl;
31775     },
31776     
31777     
31778     initEvents : function()
31779     {
31780         var _this = this;
31781         
31782         if(this.isAutoInitial){
31783             Roo.log('hook children rendered');
31784             this.on('childrenrendered', function() {
31785                 Roo.log('children rendered');
31786                 _this.initial();
31787             } ,this);
31788         }
31789         
31790     },
31791     
31792     initial : function()
31793     {
31794         this.reloadItems();
31795
31796         this.currentSize = this.el.getBox(true);
31797
31798         /// was window resize... - let's see if this works..
31799         Roo.EventManager.onWindowResize(this.resize, this); 
31800
31801         if(!this.isAutoInitial){
31802             this.layout();
31803             return;
31804         }
31805         
31806         this.layout.defer(500,this);
31807     },
31808     
31809     reloadItems: function()
31810     {
31811         this.bricks = this.el.select('.masonry-brick', true);
31812         
31813         this.bricks.each(function(b) {
31814             //Roo.log(b.getSize());
31815             if (!b.attr('originalwidth')) {
31816                 b.attr('originalwidth',  b.getSize().width);
31817             }
31818             
31819         });
31820         
31821         Roo.log(this.bricks.elements.length);
31822     },
31823     
31824     resize : function()
31825     {
31826         Roo.log('resize');
31827         var cs = this.el.getBox(true);
31828         
31829         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31830             Roo.log("no change in with or X");
31831             return;
31832         }
31833         this.currentSize = cs;
31834         this.layout();
31835     },
31836     
31837     layout : function()
31838     {
31839          Roo.log('layout');
31840         this._resetLayout();
31841         //this._manageStamps();
31842       
31843         // don't animate first layout
31844         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31845         this.layoutItems( isInstant );
31846       
31847         // flag for initalized
31848         this._isLayoutInited = true;
31849     },
31850     
31851     layoutItems : function( isInstant )
31852     {
31853         //var items = this._getItemsForLayout( this.items );
31854         // original code supports filtering layout items.. we just ignore it..
31855         
31856         this._layoutItems( this.bricks , isInstant );
31857       
31858         this._postLayout();
31859     },
31860     _layoutItems : function ( items , isInstant)
31861     {
31862        //this.fireEvent( 'layout', this, items );
31863     
31864
31865         if ( !items || !items.elements.length ) {
31866           // no items, emit event with empty array
31867             return;
31868         }
31869
31870         var queue = [];
31871         items.each(function(item) {
31872             Roo.log("layout item");
31873             Roo.log(item);
31874             // get x/y object from method
31875             var position = this._getItemLayoutPosition( item );
31876             // enqueue
31877             position.item = item;
31878             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31879             queue.push( position );
31880         }, this);
31881       
31882         this._processLayoutQueue( queue );
31883     },
31884     /** Sets position of item in DOM
31885     * @param {Element} item
31886     * @param {Number} x - horizontal position
31887     * @param {Number} y - vertical position
31888     * @param {Boolean} isInstant - disables transitions
31889     */
31890     _processLayoutQueue : function( queue )
31891     {
31892         for ( var i=0, len = queue.length; i < len; i++ ) {
31893             var obj = queue[i];
31894             obj.item.position('absolute');
31895             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31896         }
31897     },
31898       
31899     
31900     /**
31901     * Any logic you want to do after each layout,
31902     * i.e. size the container
31903     */
31904     _postLayout : function()
31905     {
31906         this.resizeContainer();
31907     },
31908     
31909     resizeContainer : function()
31910     {
31911         if ( !this.isResizingContainer ) {
31912             return;
31913         }
31914         var size = this._getContainerSize();
31915         if ( size ) {
31916             this.el.setSize(size.width,size.height);
31917             this.boxesEl.setSize(size.width,size.height);
31918         }
31919     },
31920     
31921     
31922     
31923     _resetLayout : function()
31924     {
31925         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31926         this.colWidth = this.el.getWidth();
31927         //this.gutter = this.el.getWidth(); 
31928         
31929         this.measureColumns();
31930
31931         // reset column Y
31932         var i = this.cols;
31933         this.colYs = [];
31934         while (i--) {
31935             this.colYs.push( 0 );
31936         }
31937     
31938         this.maxY = 0;
31939     },
31940
31941     measureColumns : function()
31942     {
31943         this.getContainerWidth();
31944       // if columnWidth is 0, default to outerWidth of first item
31945         if ( !this.columnWidth ) {
31946             var firstItem = this.bricks.first();
31947             Roo.log(firstItem);
31948             this.columnWidth  = this.containerWidth;
31949             if (firstItem && firstItem.attr('originalwidth') ) {
31950                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31951             }
31952             // columnWidth fall back to item of first element
31953             Roo.log("set column width?");
31954                         this.initialColumnWidth = this.columnWidth  ;
31955
31956             // if first elem has no width, default to size of container
31957             
31958         }
31959         
31960         
31961         if (this.initialColumnWidth) {
31962             this.columnWidth = this.initialColumnWidth;
31963         }
31964         
31965         
31966             
31967         // column width is fixed at the top - however if container width get's smaller we should
31968         // reduce it...
31969         
31970         // this bit calcs how man columns..
31971             
31972         var columnWidth = this.columnWidth += this.gutter;
31973       
31974         // calculate columns
31975         var containerWidth = this.containerWidth + this.gutter;
31976         
31977         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31978         // fix rounding errors, typically with gutters
31979         var excess = columnWidth - containerWidth % columnWidth;
31980         
31981         
31982         // if overshoot is less than a pixel, round up, otherwise floor it
31983         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31984         cols = Math[ mathMethod ]( cols );
31985         this.cols = Math.max( cols, 1 );
31986         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31987         
31988          // padding positioning..
31989         var totalColWidth = this.cols * this.columnWidth;
31990         var padavail = this.containerWidth - totalColWidth;
31991         // so for 2 columns - we need 3 'pads'
31992         
31993         var padNeeded = (1+this.cols) * this.padWidth;
31994         
31995         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31996         
31997         this.columnWidth += padExtra
31998         //this.padWidth = Math.floor(padavail /  ( this.cols));
31999         
32000         // adjust colum width so that padding is fixed??
32001         
32002         // we have 3 columns ... total = width * 3
32003         // we have X left over... that should be used by 
32004         
32005         //if (this.expandC) {
32006             
32007         //}
32008         
32009         
32010         
32011     },
32012     
32013     getContainerWidth : function()
32014     {
32015        /* // container is parent if fit width
32016         var container = this.isFitWidth ? this.element.parentNode : this.element;
32017         // check that this.size and size are there
32018         // IE8 triggers resize on body size change, so they might not be
32019         
32020         var size = getSize( container );  //FIXME
32021         this.containerWidth = size && size.innerWidth; //FIXME
32022         */
32023          
32024         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32025         
32026     },
32027     
32028     _getItemLayoutPosition : function( item )  // what is item?
32029     {
32030         // we resize the item to our columnWidth..
32031       
32032         item.setWidth(this.columnWidth);
32033         item.autoBoxAdjust  = false;
32034         
32035         var sz = item.getSize();
32036  
32037         // how many columns does this brick span
32038         var remainder = this.containerWidth % this.columnWidth;
32039         
32040         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32041         // round if off by 1 pixel, otherwise use ceil
32042         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32043         colSpan = Math.min( colSpan, this.cols );
32044         
32045         // normally this should be '1' as we dont' currently allow multi width columns..
32046         
32047         var colGroup = this._getColGroup( colSpan );
32048         // get the minimum Y value from the columns
32049         var minimumY = Math.min.apply( Math, colGroup );
32050         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32051         
32052         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32053          
32054         // position the brick
32055         var position = {
32056             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32057             y: this.currentSize.y + minimumY + this.padHeight
32058         };
32059         
32060         Roo.log(position);
32061         // apply setHeight to necessary columns
32062         var setHeight = minimumY + sz.height + this.padHeight;
32063         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32064         
32065         var setSpan = this.cols + 1 - colGroup.length;
32066         for ( var i = 0; i < setSpan; i++ ) {
32067           this.colYs[ shortColIndex + i ] = setHeight ;
32068         }
32069       
32070         return position;
32071     },
32072     
32073     /**
32074      * @param {Number} colSpan - number of columns the element spans
32075      * @returns {Array} colGroup
32076      */
32077     _getColGroup : function( colSpan )
32078     {
32079         if ( colSpan < 2 ) {
32080           // if brick spans only one column, use all the column Ys
32081           return this.colYs;
32082         }
32083       
32084         var colGroup = [];
32085         // how many different places could this brick fit horizontally
32086         var groupCount = this.cols + 1 - colSpan;
32087         // for each group potential horizontal position
32088         for ( var i = 0; i < groupCount; i++ ) {
32089           // make an array of colY values for that one group
32090           var groupColYs = this.colYs.slice( i, i + colSpan );
32091           // and get the max value of the array
32092           colGroup[i] = Math.max.apply( Math, groupColYs );
32093         }
32094         return colGroup;
32095     },
32096     /*
32097     _manageStamp : function( stamp )
32098     {
32099         var stampSize =  stamp.getSize();
32100         var offset = stamp.getBox();
32101         // get the columns that this stamp affects
32102         var firstX = this.isOriginLeft ? offset.x : offset.right;
32103         var lastX = firstX + stampSize.width;
32104         var firstCol = Math.floor( firstX / this.columnWidth );
32105         firstCol = Math.max( 0, firstCol );
32106         
32107         var lastCol = Math.floor( lastX / this.columnWidth );
32108         // lastCol should not go over if multiple of columnWidth #425
32109         lastCol -= lastX % this.columnWidth ? 0 : 1;
32110         lastCol = Math.min( this.cols - 1, lastCol );
32111         
32112         // set colYs to bottom of the stamp
32113         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32114             stampSize.height;
32115             
32116         for ( var i = firstCol; i <= lastCol; i++ ) {
32117           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32118         }
32119     },
32120     */
32121     
32122     _getContainerSize : function()
32123     {
32124         this.maxY = Math.max.apply( Math, this.colYs );
32125         var size = {
32126             height: this.maxY
32127         };
32128       
32129         if ( this.isFitWidth ) {
32130             size.width = this._getContainerFitWidth();
32131         }
32132       
32133         return size;
32134     },
32135     
32136     _getContainerFitWidth : function()
32137     {
32138         var unusedCols = 0;
32139         // count unused columns
32140         var i = this.cols;
32141         while ( --i ) {
32142           if ( this.colYs[i] !== 0 ) {
32143             break;
32144           }
32145           unusedCols++;
32146         }
32147         // fit container to columns that have been used
32148         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32149     },
32150     
32151     needsResizeLayout : function()
32152     {
32153         var previousWidth = this.containerWidth;
32154         this.getContainerWidth();
32155         return previousWidth !== this.containerWidth;
32156     }
32157  
32158 });
32159
32160  
32161
32162  /*
32163  * - LGPL
32164  *
32165  * element
32166  * 
32167  */
32168
32169 /**
32170  * @class Roo.bootstrap.MasonryBrick
32171  * @extends Roo.bootstrap.Component
32172  * Bootstrap MasonryBrick class
32173  * 
32174  * @constructor
32175  * Create a new MasonryBrick
32176  * @param {Object} config The config object
32177  */
32178
32179 Roo.bootstrap.MasonryBrick = function(config){
32180     
32181     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32182     
32183     Roo.bootstrap.MasonryBrick.register(this);
32184     
32185     this.addEvents({
32186         // raw events
32187         /**
32188          * @event click
32189          * When a MasonryBrick is clcik
32190          * @param {Roo.bootstrap.MasonryBrick} this
32191          * @param {Roo.EventObject} e
32192          */
32193         "click" : true
32194     });
32195 };
32196
32197 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32198     
32199     /**
32200      * @cfg {String} title
32201      */   
32202     title : '',
32203     /**
32204      * @cfg {String} html
32205      */   
32206     html : '',
32207     /**
32208      * @cfg {String} bgimage
32209      */   
32210     bgimage : '',
32211     /**
32212      * @cfg {String} videourl
32213      */   
32214     videourl : '',
32215     /**
32216      * @cfg {String} cls
32217      */   
32218     cls : '',
32219     /**
32220      * @cfg {String} href
32221      */   
32222     href : '',
32223     /**
32224      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32225      */   
32226     size : 'xs',
32227     
32228     /**
32229      * @cfg {String} placetitle (center|bottom)
32230      */   
32231     placetitle : '',
32232     
32233     /**
32234      * @cfg {Boolean} isFitContainer defalut true
32235      */   
32236     isFitContainer : true, 
32237     
32238     /**
32239      * @cfg {Boolean} preventDefault defalut false
32240      */   
32241     preventDefault : false, 
32242     
32243     /**
32244      * @cfg {Boolean} inverse defalut false
32245      */   
32246     maskInverse : false, 
32247     
32248     getAutoCreate : function()
32249     {
32250         if(!this.isFitContainer){
32251             return this.getSplitAutoCreate();
32252         }
32253         
32254         var cls = 'masonry-brick masonry-brick-full';
32255         
32256         if(this.href.length){
32257             cls += ' masonry-brick-link';
32258         }
32259         
32260         if(this.bgimage.length){
32261             cls += ' masonry-brick-image';
32262         }
32263         
32264         if(this.maskInverse){
32265             cls += ' mask-inverse';
32266         }
32267         
32268         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32269             cls += ' enable-mask';
32270         }
32271         
32272         if(this.size){
32273             cls += ' masonry-' + this.size + '-brick';
32274         }
32275         
32276         if(this.placetitle.length){
32277             
32278             switch (this.placetitle) {
32279                 case 'center' :
32280                     cls += ' masonry-center-title';
32281                     break;
32282                 case 'bottom' :
32283                     cls += ' masonry-bottom-title';
32284                     break;
32285                 default:
32286                     break;
32287             }
32288             
32289         } else {
32290             if(!this.html.length && !this.bgimage.length){
32291                 cls += ' masonry-center-title';
32292             }
32293
32294             if(!this.html.length && this.bgimage.length){
32295                 cls += ' masonry-bottom-title';
32296             }
32297         }
32298         
32299         if(this.cls){
32300             cls += ' ' + this.cls;
32301         }
32302         
32303         var cfg = {
32304             tag: (this.href.length) ? 'a' : 'div',
32305             cls: cls,
32306             cn: [
32307                 {
32308                     tag: 'div',
32309                     cls: 'masonry-brick-mask'
32310                 },
32311                 {
32312                     tag: 'div',
32313                     cls: 'masonry-brick-paragraph',
32314                     cn: []
32315                 }
32316             ]
32317         };
32318         
32319         if(this.href.length){
32320             cfg.href = this.href;
32321         }
32322         
32323         var cn = cfg.cn[1].cn;
32324         
32325         if(this.title.length){
32326             cn.push({
32327                 tag: 'h4',
32328                 cls: 'masonry-brick-title',
32329                 html: this.title
32330             });
32331         }
32332         
32333         if(this.html.length){
32334             cn.push({
32335                 tag: 'p',
32336                 cls: 'masonry-brick-text',
32337                 html: this.html
32338             });
32339         }
32340         
32341         if (!this.title.length && !this.html.length) {
32342             cfg.cn[1].cls += ' hide';
32343         }
32344         
32345         if(this.bgimage.length){
32346             cfg.cn.push({
32347                 tag: 'img',
32348                 cls: 'masonry-brick-image-view',
32349                 src: this.bgimage
32350             });
32351         }
32352         
32353         if(this.videourl.length){
32354             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32355             // youtube support only?
32356             cfg.cn.push({
32357                 tag: 'iframe',
32358                 cls: 'masonry-brick-image-view',
32359                 src: vurl,
32360                 frameborder : 0,
32361                 allowfullscreen : true
32362             });
32363         }
32364         
32365         return cfg;
32366         
32367     },
32368     
32369     getSplitAutoCreate : function()
32370     {
32371         var cls = 'masonry-brick masonry-brick-split';
32372         
32373         if(this.href.length){
32374             cls += ' masonry-brick-link';
32375         }
32376         
32377         if(this.bgimage.length){
32378             cls += ' masonry-brick-image';
32379         }
32380         
32381         if(this.size){
32382             cls += ' masonry-' + this.size + '-brick';
32383         }
32384         
32385         switch (this.placetitle) {
32386             case 'center' :
32387                 cls += ' masonry-center-title';
32388                 break;
32389             case 'bottom' :
32390                 cls += ' masonry-bottom-title';
32391                 break;
32392             default:
32393                 if(!this.bgimage.length){
32394                     cls += ' masonry-center-title';
32395                 }
32396
32397                 if(this.bgimage.length){
32398                     cls += ' masonry-bottom-title';
32399                 }
32400                 break;
32401         }
32402         
32403         if(this.cls){
32404             cls += ' ' + this.cls;
32405         }
32406         
32407         var cfg = {
32408             tag: (this.href.length) ? 'a' : 'div',
32409             cls: cls,
32410             cn: [
32411                 {
32412                     tag: 'div',
32413                     cls: 'masonry-brick-split-head',
32414                     cn: [
32415                         {
32416                             tag: 'div',
32417                             cls: 'masonry-brick-paragraph',
32418                             cn: []
32419                         }
32420                     ]
32421                 },
32422                 {
32423                     tag: 'div',
32424                     cls: 'masonry-brick-split-body',
32425                     cn: []
32426                 }
32427             ]
32428         };
32429         
32430         if(this.href.length){
32431             cfg.href = this.href;
32432         }
32433         
32434         if(this.title.length){
32435             cfg.cn[0].cn[0].cn.push({
32436                 tag: 'h4',
32437                 cls: 'masonry-brick-title',
32438                 html: this.title
32439             });
32440         }
32441         
32442         if(this.html.length){
32443             cfg.cn[1].cn.push({
32444                 tag: 'p',
32445                 cls: 'masonry-brick-text',
32446                 html: this.html
32447             });
32448         }
32449
32450         if(this.bgimage.length){
32451             cfg.cn[0].cn.push({
32452                 tag: 'img',
32453                 cls: 'masonry-brick-image-view',
32454                 src: this.bgimage
32455             });
32456         }
32457         
32458         if(this.videourl.length){
32459             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32460             // youtube support only?
32461             cfg.cn[0].cn.cn.push({
32462                 tag: 'iframe',
32463                 cls: 'masonry-brick-image-view',
32464                 src: vurl,
32465                 frameborder : 0,
32466                 allowfullscreen : true
32467             });
32468         }
32469         
32470         return cfg;
32471     },
32472     
32473     initEvents: function() 
32474     {
32475         switch (this.size) {
32476             case 'xs' :
32477                 this.x = 1;
32478                 this.y = 1;
32479                 break;
32480             case 'sm' :
32481                 this.x = 2;
32482                 this.y = 2;
32483                 break;
32484             case 'md' :
32485             case 'md-left' :
32486             case 'md-right' :
32487                 this.x = 3;
32488                 this.y = 3;
32489                 break;
32490             case 'tall' :
32491                 this.x = 2;
32492                 this.y = 3;
32493                 break;
32494             case 'wide' :
32495                 this.x = 3;
32496                 this.y = 2;
32497                 break;
32498             case 'wide-thin' :
32499                 this.x = 3;
32500                 this.y = 1;
32501                 break;
32502                         
32503             default :
32504                 break;
32505         }
32506         
32507         if(Roo.isTouch){
32508             this.el.on('touchstart', this.onTouchStart, this);
32509             this.el.on('touchmove', this.onTouchMove, this);
32510             this.el.on('touchend', this.onTouchEnd, this);
32511             this.el.on('contextmenu', this.onContextMenu, this);
32512         } else {
32513             this.el.on('mouseenter'  ,this.enter, this);
32514             this.el.on('mouseleave', this.leave, this);
32515             this.el.on('click', this.onClick, this);
32516         }
32517         
32518         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32519             this.parent().bricks.push(this);   
32520         }
32521         
32522     },
32523     
32524     onClick: function(e, el)
32525     {
32526         var time = this.endTimer - this.startTimer;
32527         // Roo.log(e.preventDefault());
32528         if(Roo.isTouch){
32529             if(time > 1000){
32530                 e.preventDefault();
32531                 return;
32532             }
32533         }
32534         
32535         if(!this.preventDefault){
32536             return;
32537         }
32538         
32539         e.preventDefault();
32540         
32541         if (this.activcClass != '') {
32542             this.selectBrick();
32543         }
32544         
32545         this.fireEvent('click', this);
32546     },
32547     
32548     enter: function(e, el)
32549     {
32550         e.preventDefault();
32551         
32552         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32553             return;
32554         }
32555         
32556         if(this.bgimage.length && this.html.length){
32557             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32558         }
32559     },
32560     
32561     leave: function(e, el)
32562     {
32563         e.preventDefault();
32564         
32565         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32566             return;
32567         }
32568         
32569         if(this.bgimage.length && this.html.length){
32570             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32571         }
32572     },
32573     
32574     onTouchStart: function(e, el)
32575     {
32576 //        e.preventDefault();
32577         
32578         this.touchmoved = false;
32579         
32580         if(!this.isFitContainer){
32581             return;
32582         }
32583         
32584         if(!this.bgimage.length || !this.html.length){
32585             return;
32586         }
32587         
32588         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32589         
32590         this.timer = new Date().getTime();
32591         
32592     },
32593     
32594     onTouchMove: function(e, el)
32595     {
32596         this.touchmoved = true;
32597     },
32598     
32599     onContextMenu : function(e,el)
32600     {
32601         e.preventDefault();
32602         e.stopPropagation();
32603         return false;
32604     },
32605     
32606     onTouchEnd: function(e, el)
32607     {
32608 //        e.preventDefault();
32609         
32610         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32611         
32612             this.leave(e,el);
32613             
32614             return;
32615         }
32616         
32617         if(!this.bgimage.length || !this.html.length){
32618             
32619             if(this.href.length){
32620                 window.location.href = this.href;
32621             }
32622             
32623             return;
32624         }
32625         
32626         if(!this.isFitContainer){
32627             return;
32628         }
32629         
32630         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32631         
32632         window.location.href = this.href;
32633     },
32634     
32635     //selection on single brick only
32636     selectBrick : function() {
32637         
32638         if (!this.parentId) {
32639             return;
32640         }
32641         
32642         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32643         var index = m.selectedBrick.indexOf(this.id);
32644         
32645         if ( index > -1) {
32646             m.selectedBrick.splice(index,1);
32647             this.el.removeClass(this.activeClass);
32648             return;
32649         }
32650         
32651         for(var i = 0; i < m.selectedBrick.length; i++) {
32652             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32653             b.el.removeClass(b.activeClass);
32654         }
32655         
32656         m.selectedBrick = [];
32657         
32658         m.selectedBrick.push(this.id);
32659         this.el.addClass(this.activeClass);
32660         return;
32661     }
32662     
32663 });
32664
32665 Roo.apply(Roo.bootstrap.MasonryBrick, {
32666     
32667     //groups: {},
32668     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32669      /**
32670     * register a Masonry Brick
32671     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32672     */
32673     
32674     register : function(brick)
32675     {
32676         //this.groups[brick.id] = brick;
32677         this.groups.add(brick.id, brick);
32678     },
32679     /**
32680     * fetch a  masonry brick based on the masonry brick ID
32681     * @param {string} the masonry brick to add
32682     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32683     */
32684     
32685     get: function(brick_id) 
32686     {
32687         // if (typeof(this.groups[brick_id]) == 'undefined') {
32688         //     return false;
32689         // }
32690         // return this.groups[brick_id] ;
32691         
32692         if(this.groups.key(brick_id)) {
32693             return this.groups.key(brick_id);
32694         }
32695         
32696         return false;
32697     }
32698     
32699     
32700     
32701 });
32702
32703  /*
32704  * - LGPL
32705  *
32706  * element
32707  * 
32708  */
32709
32710 /**
32711  * @class Roo.bootstrap.Brick
32712  * @extends Roo.bootstrap.Component
32713  * Bootstrap Brick class
32714  * 
32715  * @constructor
32716  * Create a new Brick
32717  * @param {Object} config The config object
32718  */
32719
32720 Roo.bootstrap.Brick = function(config){
32721     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32722     
32723     this.addEvents({
32724         // raw events
32725         /**
32726          * @event click
32727          * When a Brick is click
32728          * @param {Roo.bootstrap.Brick} this
32729          * @param {Roo.EventObject} e
32730          */
32731         "click" : true
32732     });
32733 };
32734
32735 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32736     
32737     /**
32738      * @cfg {String} title
32739      */   
32740     title : '',
32741     /**
32742      * @cfg {String} html
32743      */   
32744     html : '',
32745     /**
32746      * @cfg {String} bgimage
32747      */   
32748     bgimage : '',
32749     /**
32750      * @cfg {String} cls
32751      */   
32752     cls : '',
32753     /**
32754      * @cfg {String} href
32755      */   
32756     href : '',
32757     /**
32758      * @cfg {String} video
32759      */   
32760     video : '',
32761     /**
32762      * @cfg {Boolean} square
32763      */   
32764     square : true,
32765     
32766     getAutoCreate : function()
32767     {
32768         var cls = 'roo-brick';
32769         
32770         if(this.href.length){
32771             cls += ' roo-brick-link';
32772         }
32773         
32774         if(this.bgimage.length){
32775             cls += ' roo-brick-image';
32776         }
32777         
32778         if(!this.html.length && !this.bgimage.length){
32779             cls += ' roo-brick-center-title';
32780         }
32781         
32782         if(!this.html.length && this.bgimage.length){
32783             cls += ' roo-brick-bottom-title';
32784         }
32785         
32786         if(this.cls){
32787             cls += ' ' + this.cls;
32788         }
32789         
32790         var cfg = {
32791             tag: (this.href.length) ? 'a' : 'div',
32792             cls: cls,
32793             cn: [
32794                 {
32795                     tag: 'div',
32796                     cls: 'roo-brick-paragraph',
32797                     cn: []
32798                 }
32799             ]
32800         };
32801         
32802         if(this.href.length){
32803             cfg.href = this.href;
32804         }
32805         
32806         var cn = cfg.cn[0].cn;
32807         
32808         if(this.title.length){
32809             cn.push({
32810                 tag: 'h4',
32811                 cls: 'roo-brick-title',
32812                 html: this.title
32813             });
32814         }
32815         
32816         if(this.html.length){
32817             cn.push({
32818                 tag: 'p',
32819                 cls: 'roo-brick-text',
32820                 html: this.html
32821             });
32822         } else {
32823             cn.cls += ' hide';
32824         }
32825         
32826         if(this.bgimage.length){
32827             cfg.cn.push({
32828                 tag: 'img',
32829                 cls: 'roo-brick-image-view',
32830                 src: this.bgimage
32831             });
32832         }
32833         
32834         return cfg;
32835     },
32836     
32837     initEvents: function() 
32838     {
32839         if(this.title.length || this.html.length){
32840             this.el.on('mouseenter'  ,this.enter, this);
32841             this.el.on('mouseleave', this.leave, this);
32842         }
32843         
32844         Roo.EventManager.onWindowResize(this.resize, this); 
32845         
32846         if(this.bgimage.length){
32847             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32848             this.imageEl.on('load', this.onImageLoad, this);
32849             return;
32850         }
32851         
32852         this.resize();
32853     },
32854     
32855     onImageLoad : function()
32856     {
32857         this.resize();
32858     },
32859     
32860     resize : function()
32861     {
32862         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32863         
32864         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32865         
32866         if(this.bgimage.length){
32867             var image = this.el.select('.roo-brick-image-view', true).first();
32868             
32869             image.setWidth(paragraph.getWidth());
32870             
32871             if(this.square){
32872                 image.setHeight(paragraph.getWidth());
32873             }
32874             
32875             this.el.setHeight(image.getHeight());
32876             paragraph.setHeight(image.getHeight());
32877             
32878         }
32879         
32880     },
32881     
32882     enter: function(e, el)
32883     {
32884         e.preventDefault();
32885         
32886         if(this.bgimage.length){
32887             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32888             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32889         }
32890     },
32891     
32892     leave: function(e, el)
32893     {
32894         e.preventDefault();
32895         
32896         if(this.bgimage.length){
32897             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32898             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32899         }
32900     }
32901     
32902 });
32903
32904  
32905
32906  /*
32907  * - LGPL
32908  *
32909  * Input
32910  * 
32911  */
32912
32913 /**
32914  * @class Roo.bootstrap.NumberField
32915  * @extends Roo.bootstrap.Input
32916  * Bootstrap NumberField class
32917  * 
32918  * 
32919  * 
32920  * 
32921  * @constructor
32922  * Create a new NumberField
32923  * @param {Object} config The config object
32924  */
32925
32926 Roo.bootstrap.NumberField = function(config){
32927     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32928 };
32929
32930 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32931     
32932     /**
32933      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32934      */
32935     allowDecimals : true,
32936     /**
32937      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32938      */
32939     decimalSeparator : ".",
32940     /**
32941      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32942      */
32943     decimalPrecision : 2,
32944     /**
32945      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32946      */
32947     allowNegative : true,
32948     /**
32949      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32950      */
32951     minValue : Number.NEGATIVE_INFINITY,
32952     /**
32953      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32954      */
32955     maxValue : Number.MAX_VALUE,
32956     /**
32957      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32958      */
32959     minText : "The minimum value for this field is {0}",
32960     /**
32961      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32962      */
32963     maxText : "The maximum value for this field is {0}",
32964     /**
32965      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32966      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32967      */
32968     nanText : "{0} is not a valid number",
32969     /**
32970      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32971      */
32972     castInt : true,
32973
32974     // private
32975     initEvents : function()
32976     {   
32977         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32978         
32979         var allowed = "0123456789";
32980         
32981         if(this.allowDecimals){
32982             allowed += this.decimalSeparator;
32983         }
32984         
32985         if(this.allowNegative){
32986             allowed += "-";
32987         }
32988         
32989         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32990         
32991         var keyPress = function(e){
32992             
32993             var k = e.getKey();
32994             
32995             var c = e.getCharCode();
32996             
32997             if(
32998                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32999                     allowed.indexOf(String.fromCharCode(c)) === -1
33000             ){
33001                 e.stopEvent();
33002                 return;
33003             }
33004             
33005             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33006                 return;
33007             }
33008             
33009             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33010                 e.stopEvent();
33011             }
33012         };
33013         
33014         this.el.on("keypress", keyPress, this);
33015     },
33016     
33017     validateValue : function(value)
33018     {
33019         
33020         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33021             return false;
33022         }
33023         
33024         var num = this.parseValue(value);
33025         
33026         if(isNaN(num)){
33027             this.markInvalid(String.format(this.nanText, value));
33028             return false;
33029         }
33030         
33031         if(num < this.minValue){
33032             this.markInvalid(String.format(this.minText, this.minValue));
33033             return false;
33034         }
33035         
33036         if(num > this.maxValue){
33037             this.markInvalid(String.format(this.maxText, this.maxValue));
33038             return false;
33039         }
33040         
33041         return true;
33042     },
33043
33044     getValue : function()
33045     {
33046         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33047     },
33048
33049     parseValue : function(value)
33050     {
33051         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33052         return isNaN(value) ? '' : value;
33053     },
33054
33055     fixPrecision : function(value)
33056     {
33057         var nan = isNaN(value);
33058         
33059         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33060             return nan ? '' : value;
33061         }
33062         return parseFloat(value).toFixed(this.decimalPrecision);
33063     },
33064
33065     setValue : function(v)
33066     {
33067         v = this.fixPrecision(v);
33068         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33069     },
33070
33071     decimalPrecisionFcn : function(v)
33072     {
33073         return Math.floor(v);
33074     },
33075
33076     beforeBlur : function()
33077     {
33078         if(!this.castInt){
33079             return;
33080         }
33081         
33082         var v = this.parseValue(this.getRawValue());
33083         if(v){
33084             this.setValue(v);
33085         }
33086     }
33087     
33088 });
33089
33090  
33091
33092 /*
33093 * Licence: LGPL
33094 */
33095
33096 /**
33097  * @class Roo.bootstrap.DocumentSlider
33098  * @extends Roo.bootstrap.Component
33099  * Bootstrap DocumentSlider class
33100  * 
33101  * @constructor
33102  * Create a new DocumentViewer
33103  * @param {Object} config The config object
33104  */
33105
33106 Roo.bootstrap.DocumentSlider = function(config){
33107     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33108     
33109     this.files = [];
33110     
33111     this.addEvents({
33112         /**
33113          * @event initial
33114          * Fire after initEvent
33115          * @param {Roo.bootstrap.DocumentSlider} this
33116          */
33117         "initial" : true,
33118         /**
33119          * @event update
33120          * Fire after update
33121          * @param {Roo.bootstrap.DocumentSlider} this
33122          */
33123         "update" : true,
33124         /**
33125          * @event click
33126          * Fire after click
33127          * @param {Roo.bootstrap.DocumentSlider} this
33128          */
33129         "click" : true
33130     });
33131 };
33132
33133 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33134     
33135     files : false,
33136     
33137     indicator : 0,
33138     
33139     getAutoCreate : function()
33140     {
33141         var cfg = {
33142             tag : 'div',
33143             cls : 'roo-document-slider',
33144             cn : [
33145                 {
33146                     tag : 'div',
33147                     cls : 'roo-document-slider-header',
33148                     cn : [
33149                         {
33150                             tag : 'div',
33151                             cls : 'roo-document-slider-header-title'
33152                         }
33153                     ]
33154                 },
33155                 {
33156                     tag : 'div',
33157                     cls : 'roo-document-slider-body',
33158                     cn : [
33159                         {
33160                             tag : 'div',
33161                             cls : 'roo-document-slider-prev',
33162                             cn : [
33163                                 {
33164                                     tag : 'i',
33165                                     cls : 'fa fa-chevron-left'
33166                                 }
33167                             ]
33168                         },
33169                         {
33170                             tag : 'div',
33171                             cls : 'roo-document-slider-thumb',
33172                             cn : [
33173                                 {
33174                                     tag : 'img',
33175                                     cls : 'roo-document-slider-image'
33176                                 }
33177                             ]
33178                         },
33179                         {
33180                             tag : 'div',
33181                             cls : 'roo-document-slider-next',
33182                             cn : [
33183                                 {
33184                                     tag : 'i',
33185                                     cls : 'fa fa-chevron-right'
33186                                 }
33187                             ]
33188                         }
33189                     ]
33190                 }
33191             ]
33192         };
33193         
33194         return cfg;
33195     },
33196     
33197     initEvents : function()
33198     {
33199         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33200         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33201         
33202         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33203         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33204         
33205         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33206         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33207         
33208         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33209         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33210         
33211         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33212         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33213         
33214         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33215         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33216         
33217         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33218         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33219         
33220         this.thumbEl.on('click', this.onClick, this);
33221         
33222         this.prevIndicator.on('click', this.prev, this);
33223         
33224         this.nextIndicator.on('click', this.next, this);
33225         
33226     },
33227     
33228     initial : function()
33229     {
33230         if(this.files.length){
33231             this.indicator = 1;
33232             this.update()
33233         }
33234         
33235         this.fireEvent('initial', this);
33236     },
33237     
33238     update : function()
33239     {
33240         this.imageEl.attr('src', this.files[this.indicator - 1]);
33241         
33242         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33243         
33244         this.prevIndicator.show();
33245         
33246         if(this.indicator == 1){
33247             this.prevIndicator.hide();
33248         }
33249         
33250         this.nextIndicator.show();
33251         
33252         if(this.indicator == this.files.length){
33253             this.nextIndicator.hide();
33254         }
33255         
33256         this.thumbEl.scrollTo('top');
33257         
33258         this.fireEvent('update', this);
33259     },
33260     
33261     onClick : function(e)
33262     {
33263         e.preventDefault();
33264         
33265         this.fireEvent('click', this);
33266     },
33267     
33268     prev : function(e)
33269     {
33270         e.preventDefault();
33271         
33272         this.indicator = Math.max(1, this.indicator - 1);
33273         
33274         this.update();
33275     },
33276     
33277     next : function(e)
33278     {
33279         e.preventDefault();
33280         
33281         this.indicator = Math.min(this.files.length, this.indicator + 1);
33282         
33283         this.update();
33284     }
33285 });
33286 /*
33287  * - LGPL
33288  *
33289  * RadioSet
33290  *
33291  *
33292  */
33293
33294 /**
33295  * @class Roo.bootstrap.RadioSet
33296  * @extends Roo.bootstrap.Input
33297  * Bootstrap RadioSet class
33298  * @cfg {String} indicatorpos (left|right) default left
33299  * @cfg {Boolean} inline (true|false) inline the element (default true)
33300  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33301  * @constructor
33302  * Create a new RadioSet
33303  * @param {Object} config The config object
33304  */
33305
33306 Roo.bootstrap.RadioSet = function(config){
33307     
33308     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33309     
33310     this.radioes = [];
33311     
33312     Roo.bootstrap.RadioSet.register(this);
33313     
33314     this.addEvents({
33315         /**
33316         * @event check
33317         * Fires when the element is checked or unchecked.
33318         * @param {Roo.bootstrap.RadioSet} this This radio
33319         * @param {Roo.bootstrap.Radio} item The checked item
33320         */
33321        check : true
33322     });
33323     
33324 };
33325
33326 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33327
33328     radioes : false,
33329     
33330     inline : true,
33331     
33332     weight : '',
33333     
33334     indicatorpos : 'left',
33335     
33336     getAutoCreate : function()
33337     {
33338         var label = {
33339             tag : 'label',
33340             cls : 'roo-radio-set-label',
33341             cn : [
33342                 {
33343                     tag : 'span',
33344                     html : this.fieldLabel
33345                 }
33346             ]
33347         };
33348         
33349         if(this.indicatorpos == 'left'){
33350             label.cn.unshift({
33351                 tag : 'i',
33352                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33353                 tooltip : 'This field is required'
33354             });
33355         } else {
33356             label.cn.push({
33357                 tag : 'i',
33358                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33359                 tooltip : 'This field is required'
33360             });
33361         }
33362         
33363         var items = {
33364             tag : 'div',
33365             cls : 'roo-radio-set-items'
33366         };
33367         
33368         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33369         
33370         if (align === 'left' && this.fieldLabel.length) {
33371             
33372             items = {
33373                 cls : "roo-radio-set-right", 
33374                 cn: [
33375                     items
33376                 ]
33377             };
33378             
33379             if(this.labelWidth > 12){
33380                 label.style = "width: " + this.labelWidth + 'px';
33381             }
33382             
33383             if(this.labelWidth < 13 && this.labelmd == 0){
33384                 this.labelmd = this.labelWidth;
33385             }
33386             
33387             if(this.labellg > 0){
33388                 label.cls += ' col-lg-' + this.labellg;
33389                 items.cls += ' col-lg-' + (12 - this.labellg);
33390             }
33391             
33392             if(this.labelmd > 0){
33393                 label.cls += ' col-md-' + this.labelmd;
33394                 items.cls += ' col-md-' + (12 - this.labelmd);
33395             }
33396             
33397             if(this.labelsm > 0){
33398                 label.cls += ' col-sm-' + this.labelsm;
33399                 items.cls += ' col-sm-' + (12 - this.labelsm);
33400             }
33401             
33402             if(this.labelxs > 0){
33403                 label.cls += ' col-xs-' + this.labelxs;
33404                 items.cls += ' col-xs-' + (12 - this.labelxs);
33405             }
33406         }
33407         
33408         var cfg = {
33409             tag : 'div',
33410             cls : 'roo-radio-set',
33411             cn : [
33412                 {
33413                     tag : 'input',
33414                     cls : 'roo-radio-set-input',
33415                     type : 'hidden',
33416                     name : this.name,
33417                     value : this.value ? this.value :  ''
33418                 },
33419                 label,
33420                 items
33421             ]
33422         };
33423         
33424         if(this.weight.length){
33425             cfg.cls += ' roo-radio-' + this.weight;
33426         }
33427         
33428         if(this.inline) {
33429             cfg.cls += ' roo-radio-set-inline';
33430         }
33431         
33432         var settings=this;
33433         ['xs','sm','md','lg'].map(function(size){
33434             if (settings[size]) {
33435                 cfg.cls += ' col-' + size + '-' + settings[size];
33436             }
33437         });
33438         
33439         return cfg;
33440         
33441     },
33442
33443     initEvents : function()
33444     {
33445         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33446         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33447         
33448         if(!this.fieldLabel.length){
33449             this.labelEl.hide();
33450         }
33451         
33452         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33453         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33454         
33455         this.indicatorEl().addClass('invisible');
33456         
33457         this.originalValue = this.getValue();
33458         
33459     },
33460     
33461     inputEl: function ()
33462     {
33463         return this.el.select('.roo-radio-set-input', true).first();
33464     },
33465     
33466     getChildContainer : function()
33467     {
33468         return this.itemsEl;
33469     },
33470     
33471     register : function(item)
33472     {
33473         this.radioes.push(item);
33474         
33475     },
33476     
33477     validate : function()
33478     {   
33479         var valid = false;
33480         
33481         Roo.each(this.radioes, function(i){
33482             if(!i.checked){
33483                 return;
33484             }
33485             
33486             valid = true;
33487             return false;
33488         });
33489         
33490         if(this.allowBlank) {
33491             return true;
33492         }
33493         
33494         if(this.disabled || valid){
33495             this.markValid();
33496             return true;
33497         }
33498         
33499         this.markInvalid();
33500         return false;
33501         
33502     },
33503     
33504     markValid : function()
33505     {
33506         if(this.labelEl.isVisible(true)){
33507             this.indicatorEl().removeClass('visible');
33508             this.indicatorEl().addClass('invisible');
33509         }
33510         
33511         this.el.removeClass([this.invalidClass, this.validClass]);
33512         this.el.addClass(this.validClass);
33513         
33514         this.fireEvent('valid', this);
33515     },
33516     
33517     markInvalid : function(msg)
33518     {
33519         if(this.allowBlank || this.disabled){
33520             return;
33521         }
33522         
33523         if(this.labelEl.isVisible(true)){
33524             this.indicatorEl().removeClass('invisible');
33525             this.indicatorEl().addClass('visible');
33526         }
33527         
33528         this.el.removeClass([this.invalidClass, this.validClass]);
33529         this.el.addClass(this.invalidClass);
33530         
33531         this.fireEvent('invalid', this, msg);
33532         
33533     },
33534     
33535     setValue : function(v, suppressEvent)
33536     {   
33537         if(this.value === v){
33538             return;
33539         }
33540         
33541         this.value = v;
33542         
33543         if(this.rendered){
33544             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33545         }
33546         
33547         Roo.each(this.radioes, function(i){
33548             
33549             i.checked = false;
33550             i.el.removeClass('checked');
33551             
33552             if(i.value === v || i.value.toString() === v.toString()){
33553                 i.checked = true;
33554                 i.el.addClass('checked');
33555                 
33556                 if(suppressEvent !== true){
33557                     this.fireEvent('check', this, i);
33558                 }
33559             }
33560             
33561         }, this);
33562         
33563         this.validate();
33564     },
33565     
33566     clearInvalid : function(){
33567         
33568         if(!this.el || this.preventMark){
33569             return;
33570         }
33571         
33572         this.el.removeClass([this.invalidClass]);
33573         
33574         this.fireEvent('valid', this);
33575     }
33576     
33577 });
33578
33579 Roo.apply(Roo.bootstrap.RadioSet, {
33580     
33581     groups: {},
33582     
33583     register : function(set)
33584     {
33585         this.groups[set.name] = set;
33586     },
33587     
33588     get: function(name) 
33589     {
33590         if (typeof(this.groups[name]) == 'undefined') {
33591             return false;
33592         }
33593         
33594         return this.groups[name] ;
33595     }
33596     
33597 });
33598 /*
33599  * Based on:
33600  * Ext JS Library 1.1.1
33601  * Copyright(c) 2006-2007, Ext JS, LLC.
33602  *
33603  * Originally Released Under LGPL - original licence link has changed is not relivant.
33604  *
33605  * Fork - LGPL
33606  * <script type="text/javascript">
33607  */
33608
33609
33610 /**
33611  * @class Roo.bootstrap.SplitBar
33612  * @extends Roo.util.Observable
33613  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33614  * <br><br>
33615  * Usage:
33616  * <pre><code>
33617 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33618                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33619 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33620 split.minSize = 100;
33621 split.maxSize = 600;
33622 split.animate = true;
33623 split.on('moved', splitterMoved);
33624 </code></pre>
33625  * @constructor
33626  * Create a new SplitBar
33627  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33628  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33629  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33630  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33631                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33632                         position of the SplitBar).
33633  */
33634 Roo.bootstrap.SplitBar = function(cfg){
33635     
33636     /** @private */
33637     
33638     //{
33639     //  dragElement : elm
33640     //  resizingElement: el,
33641         // optional..
33642     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33643     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33644         // existingProxy ???
33645     //}
33646     
33647     this.el = Roo.get(cfg.dragElement, true);
33648     this.el.dom.unselectable = "on";
33649     /** @private */
33650     this.resizingEl = Roo.get(cfg.resizingElement, true);
33651
33652     /**
33653      * @private
33654      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33655      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33656      * @type Number
33657      */
33658     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33659     
33660     /**
33661      * The minimum size of the resizing element. (Defaults to 0)
33662      * @type Number
33663      */
33664     this.minSize = 0;
33665     
33666     /**
33667      * The maximum size of the resizing element. (Defaults to 2000)
33668      * @type Number
33669      */
33670     this.maxSize = 2000;
33671     
33672     /**
33673      * Whether to animate the transition to the new size
33674      * @type Boolean
33675      */
33676     this.animate = false;
33677     
33678     /**
33679      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33680      * @type Boolean
33681      */
33682     this.useShim = false;
33683     
33684     /** @private */
33685     this.shim = null;
33686     
33687     if(!cfg.existingProxy){
33688         /** @private */
33689         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33690     }else{
33691         this.proxy = Roo.get(cfg.existingProxy).dom;
33692     }
33693     /** @private */
33694     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33695     
33696     /** @private */
33697     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33698     
33699     /** @private */
33700     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33701     
33702     /** @private */
33703     this.dragSpecs = {};
33704     
33705     /**
33706      * @private The adapter to use to positon and resize elements
33707      */
33708     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33709     this.adapter.init(this);
33710     
33711     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33712         /** @private */
33713         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33714         this.el.addClass("roo-splitbar-h");
33715     }else{
33716         /** @private */
33717         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33718         this.el.addClass("roo-splitbar-v");
33719     }
33720     
33721     this.addEvents({
33722         /**
33723          * @event resize
33724          * Fires when the splitter is moved (alias for {@link #event-moved})
33725          * @param {Roo.bootstrap.SplitBar} this
33726          * @param {Number} newSize the new width or height
33727          */
33728         "resize" : true,
33729         /**
33730          * @event moved
33731          * Fires when the splitter is moved
33732          * @param {Roo.bootstrap.SplitBar} this
33733          * @param {Number} newSize the new width or height
33734          */
33735         "moved" : true,
33736         /**
33737          * @event beforeresize
33738          * Fires before the splitter is dragged
33739          * @param {Roo.bootstrap.SplitBar} this
33740          */
33741         "beforeresize" : true,
33742
33743         "beforeapply" : true
33744     });
33745
33746     Roo.util.Observable.call(this);
33747 };
33748
33749 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33750     onStartProxyDrag : function(x, y){
33751         this.fireEvent("beforeresize", this);
33752         if(!this.overlay){
33753             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33754             o.unselectable();
33755             o.enableDisplayMode("block");
33756             // all splitbars share the same overlay
33757             Roo.bootstrap.SplitBar.prototype.overlay = o;
33758         }
33759         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33760         this.overlay.show();
33761         Roo.get(this.proxy).setDisplayed("block");
33762         var size = this.adapter.getElementSize(this);
33763         this.activeMinSize = this.getMinimumSize();;
33764         this.activeMaxSize = this.getMaximumSize();;
33765         var c1 = size - this.activeMinSize;
33766         var c2 = Math.max(this.activeMaxSize - size, 0);
33767         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33768             this.dd.resetConstraints();
33769             this.dd.setXConstraint(
33770                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33771                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33772             );
33773             this.dd.setYConstraint(0, 0);
33774         }else{
33775             this.dd.resetConstraints();
33776             this.dd.setXConstraint(0, 0);
33777             this.dd.setYConstraint(
33778                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33779                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33780             );
33781          }
33782         this.dragSpecs.startSize = size;
33783         this.dragSpecs.startPoint = [x, y];
33784         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33785     },
33786     
33787     /** 
33788      * @private Called after the drag operation by the DDProxy
33789      */
33790     onEndProxyDrag : function(e){
33791         Roo.get(this.proxy).setDisplayed(false);
33792         var endPoint = Roo.lib.Event.getXY(e);
33793         if(this.overlay){
33794             this.overlay.hide();
33795         }
33796         var newSize;
33797         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33798             newSize = this.dragSpecs.startSize + 
33799                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33800                     endPoint[0] - this.dragSpecs.startPoint[0] :
33801                     this.dragSpecs.startPoint[0] - endPoint[0]
33802                 );
33803         }else{
33804             newSize = this.dragSpecs.startSize + 
33805                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33806                     endPoint[1] - this.dragSpecs.startPoint[1] :
33807                     this.dragSpecs.startPoint[1] - endPoint[1]
33808                 );
33809         }
33810         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33811         if(newSize != this.dragSpecs.startSize){
33812             if(this.fireEvent('beforeapply', this, newSize) !== false){
33813                 this.adapter.setElementSize(this, newSize);
33814                 this.fireEvent("moved", this, newSize);
33815                 this.fireEvent("resize", this, newSize);
33816             }
33817         }
33818     },
33819     
33820     /**
33821      * Get the adapter this SplitBar uses
33822      * @return The adapter object
33823      */
33824     getAdapter : function(){
33825         return this.adapter;
33826     },
33827     
33828     /**
33829      * Set the adapter this SplitBar uses
33830      * @param {Object} adapter A SplitBar adapter object
33831      */
33832     setAdapter : function(adapter){
33833         this.adapter = adapter;
33834         this.adapter.init(this);
33835     },
33836     
33837     /**
33838      * Gets the minimum size for the resizing element
33839      * @return {Number} The minimum size
33840      */
33841     getMinimumSize : function(){
33842         return this.minSize;
33843     },
33844     
33845     /**
33846      * Sets the minimum size for the resizing element
33847      * @param {Number} minSize The minimum size
33848      */
33849     setMinimumSize : function(minSize){
33850         this.minSize = minSize;
33851     },
33852     
33853     /**
33854      * Gets the maximum size for the resizing element
33855      * @return {Number} The maximum size
33856      */
33857     getMaximumSize : function(){
33858         return this.maxSize;
33859     },
33860     
33861     /**
33862      * Sets the maximum size for the resizing element
33863      * @param {Number} maxSize The maximum size
33864      */
33865     setMaximumSize : function(maxSize){
33866         this.maxSize = maxSize;
33867     },
33868     
33869     /**
33870      * Sets the initialize size for the resizing element
33871      * @param {Number} size The initial size
33872      */
33873     setCurrentSize : function(size){
33874         var oldAnimate = this.animate;
33875         this.animate = false;
33876         this.adapter.setElementSize(this, size);
33877         this.animate = oldAnimate;
33878     },
33879     
33880     /**
33881      * Destroy this splitbar. 
33882      * @param {Boolean} removeEl True to remove the element
33883      */
33884     destroy : function(removeEl){
33885         if(this.shim){
33886             this.shim.remove();
33887         }
33888         this.dd.unreg();
33889         this.proxy.parentNode.removeChild(this.proxy);
33890         if(removeEl){
33891             this.el.remove();
33892         }
33893     }
33894 });
33895
33896 /**
33897  * @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.
33898  */
33899 Roo.bootstrap.SplitBar.createProxy = function(dir){
33900     var proxy = new Roo.Element(document.createElement("div"));
33901     proxy.unselectable();
33902     var cls = 'roo-splitbar-proxy';
33903     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33904     document.body.appendChild(proxy.dom);
33905     return proxy.dom;
33906 };
33907
33908 /** 
33909  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33910  * Default Adapter. It assumes the splitter and resizing element are not positioned
33911  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33912  */
33913 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33914 };
33915
33916 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33917     // do nothing for now
33918     init : function(s){
33919     
33920     },
33921     /**
33922      * Called before drag operations to get the current size of the resizing element. 
33923      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33924      */
33925      getElementSize : function(s){
33926         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33927             return s.resizingEl.getWidth();
33928         }else{
33929             return s.resizingEl.getHeight();
33930         }
33931     },
33932     
33933     /**
33934      * Called after drag operations to set the size of the resizing element.
33935      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33936      * @param {Number} newSize The new size to set
33937      * @param {Function} onComplete A function to be invoked when resizing is complete
33938      */
33939     setElementSize : function(s, newSize, onComplete){
33940         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33941             if(!s.animate){
33942                 s.resizingEl.setWidth(newSize);
33943                 if(onComplete){
33944                     onComplete(s, newSize);
33945                 }
33946             }else{
33947                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33948             }
33949         }else{
33950             
33951             if(!s.animate){
33952                 s.resizingEl.setHeight(newSize);
33953                 if(onComplete){
33954                     onComplete(s, newSize);
33955                 }
33956             }else{
33957                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33958             }
33959         }
33960     }
33961 };
33962
33963 /** 
33964  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33965  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33966  * Adapter that  moves the splitter element to align with the resized sizing element. 
33967  * Used with an absolute positioned SplitBar.
33968  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33969  * document.body, make sure you assign an id to the body element.
33970  */
33971 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33972     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33973     this.container = Roo.get(container);
33974 };
33975
33976 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33977     init : function(s){
33978         this.basic.init(s);
33979     },
33980     
33981     getElementSize : function(s){
33982         return this.basic.getElementSize(s);
33983     },
33984     
33985     setElementSize : function(s, newSize, onComplete){
33986         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33987     },
33988     
33989     moveSplitter : function(s){
33990         var yes = Roo.bootstrap.SplitBar;
33991         switch(s.placement){
33992             case yes.LEFT:
33993                 s.el.setX(s.resizingEl.getRight());
33994                 break;
33995             case yes.RIGHT:
33996                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33997                 break;
33998             case yes.TOP:
33999                 s.el.setY(s.resizingEl.getBottom());
34000                 break;
34001             case yes.BOTTOM:
34002                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34003                 break;
34004         }
34005     }
34006 };
34007
34008 /**
34009  * Orientation constant - Create a vertical SplitBar
34010  * @static
34011  * @type Number
34012  */
34013 Roo.bootstrap.SplitBar.VERTICAL = 1;
34014
34015 /**
34016  * Orientation constant - Create a horizontal SplitBar
34017  * @static
34018  * @type Number
34019  */
34020 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34021
34022 /**
34023  * Placement constant - The resizing element is to the left of the splitter element
34024  * @static
34025  * @type Number
34026  */
34027 Roo.bootstrap.SplitBar.LEFT = 1;
34028
34029 /**
34030  * Placement constant - The resizing element is to the right of the splitter element
34031  * @static
34032  * @type Number
34033  */
34034 Roo.bootstrap.SplitBar.RIGHT = 2;
34035
34036 /**
34037  * Placement constant - The resizing element is positioned above the splitter element
34038  * @static
34039  * @type Number
34040  */
34041 Roo.bootstrap.SplitBar.TOP = 3;
34042
34043 /**
34044  * Placement constant - The resizing element is positioned under splitter element
34045  * @static
34046  * @type Number
34047  */
34048 Roo.bootstrap.SplitBar.BOTTOM = 4;
34049 Roo.namespace("Roo.bootstrap.layout");/*
34050  * Based on:
34051  * Ext JS Library 1.1.1
34052  * Copyright(c) 2006-2007, Ext JS, LLC.
34053  *
34054  * Originally Released Under LGPL - original licence link has changed is not relivant.
34055  *
34056  * Fork - LGPL
34057  * <script type="text/javascript">
34058  */
34059
34060 /**
34061  * @class Roo.bootstrap.layout.Manager
34062  * @extends Roo.bootstrap.Component
34063  * Base class for layout managers.
34064  */
34065 Roo.bootstrap.layout.Manager = function(config)
34066 {
34067     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34068
34069
34070
34071
34072
34073     /** false to disable window resize monitoring @type Boolean */
34074     this.monitorWindowResize = true;
34075     this.regions = {};
34076     this.addEvents({
34077         /**
34078          * @event layout
34079          * Fires when a layout is performed.
34080          * @param {Roo.LayoutManager} this
34081          */
34082         "layout" : true,
34083         /**
34084          * @event regionresized
34085          * Fires when the user resizes a region.
34086          * @param {Roo.LayoutRegion} region The resized region
34087          * @param {Number} newSize The new size (width for east/west, height for north/south)
34088          */
34089         "regionresized" : true,
34090         /**
34091          * @event regioncollapsed
34092          * Fires when a region is collapsed.
34093          * @param {Roo.LayoutRegion} region The collapsed region
34094          */
34095         "regioncollapsed" : true,
34096         /**
34097          * @event regionexpanded
34098          * Fires when a region is expanded.
34099          * @param {Roo.LayoutRegion} region The expanded region
34100          */
34101         "regionexpanded" : true
34102     });
34103     this.updating = false;
34104
34105     if (config.el) {
34106         this.el = Roo.get(config.el);
34107         this.initEvents();
34108     }
34109
34110 };
34111
34112 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34113
34114
34115     regions : null,
34116
34117     monitorWindowResize : true,
34118
34119
34120     updating : false,
34121
34122
34123     onRender : function(ct, position)
34124     {
34125         if(!this.el){
34126             this.el = Roo.get(ct);
34127             this.initEvents();
34128         }
34129         //this.fireEvent('render',this);
34130     },
34131
34132
34133     initEvents: function()
34134     {
34135
34136
34137         // ie scrollbar fix
34138         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34139             document.body.scroll = "no";
34140         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34141             this.el.position('relative');
34142         }
34143         this.id = this.el.id;
34144         this.el.addClass("roo-layout-container");
34145         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34146         if(this.el.dom != document.body ) {
34147             this.el.on('resize', this.layout,this);
34148             this.el.on('show', this.layout,this);
34149         }
34150
34151     },
34152
34153     /**
34154      * Returns true if this layout is currently being updated
34155      * @return {Boolean}
34156      */
34157     isUpdating : function(){
34158         return this.updating;
34159     },
34160
34161     /**
34162      * Suspend the LayoutManager from doing auto-layouts while
34163      * making multiple add or remove calls
34164      */
34165     beginUpdate : function(){
34166         this.updating = true;
34167     },
34168
34169     /**
34170      * Restore auto-layouts and optionally disable the manager from performing a layout
34171      * @param {Boolean} noLayout true to disable a layout update
34172      */
34173     endUpdate : function(noLayout){
34174         this.updating = false;
34175         if(!noLayout){
34176             this.layout();
34177         }
34178     },
34179
34180     layout: function(){
34181         // abstract...
34182     },
34183
34184     onRegionResized : function(region, newSize){
34185         this.fireEvent("regionresized", region, newSize);
34186         this.layout();
34187     },
34188
34189     onRegionCollapsed : function(region){
34190         this.fireEvent("regioncollapsed", region);
34191     },
34192
34193     onRegionExpanded : function(region){
34194         this.fireEvent("regionexpanded", region);
34195     },
34196
34197     /**
34198      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34199      * performs box-model adjustments.
34200      * @return {Object} The size as an object {width: (the width), height: (the height)}
34201      */
34202     getViewSize : function()
34203     {
34204         var size;
34205         if(this.el.dom != document.body){
34206             size = this.el.getSize();
34207         }else{
34208             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34209         }
34210         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34211         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34212         return size;
34213     },
34214
34215     /**
34216      * Returns the Element this layout is bound to.
34217      * @return {Roo.Element}
34218      */
34219     getEl : function(){
34220         return this.el;
34221     },
34222
34223     /**
34224      * Returns the specified region.
34225      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34226      * @return {Roo.LayoutRegion}
34227      */
34228     getRegion : function(target){
34229         return this.regions[target.toLowerCase()];
34230     },
34231
34232     onWindowResize : function(){
34233         if(this.monitorWindowResize){
34234             this.layout();
34235         }
34236     }
34237 });
34238 /*
34239  * Based on:
34240  * Ext JS Library 1.1.1
34241  * Copyright(c) 2006-2007, Ext JS, LLC.
34242  *
34243  * Originally Released Under LGPL - original licence link has changed is not relivant.
34244  *
34245  * Fork - LGPL
34246  * <script type="text/javascript">
34247  */
34248 /**
34249  * @class Roo.bootstrap.layout.Border
34250  * @extends Roo.bootstrap.layout.Manager
34251  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34252  * please see: examples/bootstrap/nested.html<br><br>
34253  
34254 <b>The container the layout is rendered into can be either the body element or any other element.
34255 If it is not the body element, the container needs to either be an absolute positioned element,
34256 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34257 the container size if it is not the body element.</b>
34258
34259 * @constructor
34260 * Create a new Border
34261 * @param {Object} config Configuration options
34262  */
34263 Roo.bootstrap.layout.Border = function(config){
34264     config = config || {};
34265     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34266     
34267     
34268     
34269     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34270         if(config[region]){
34271             config[region].region = region;
34272             this.addRegion(config[region]);
34273         }
34274     },this);
34275     
34276 };
34277
34278 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34279
34280 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34281     /**
34282      * Creates and adds a new region if it doesn't already exist.
34283      * @param {String} target The target region key (north, south, east, west or center).
34284      * @param {Object} config The regions config object
34285      * @return {BorderLayoutRegion} The new region
34286      */
34287     addRegion : function(config)
34288     {
34289         if(!this.regions[config.region]){
34290             var r = this.factory(config);
34291             this.bindRegion(r);
34292         }
34293         return this.regions[config.region];
34294     },
34295
34296     // private (kinda)
34297     bindRegion : function(r){
34298         this.regions[r.config.region] = r;
34299         
34300         r.on("visibilitychange",    this.layout, this);
34301         r.on("paneladded",          this.layout, this);
34302         r.on("panelremoved",        this.layout, this);
34303         r.on("invalidated",         this.layout, this);
34304         r.on("resized",             this.onRegionResized, this);
34305         r.on("collapsed",           this.onRegionCollapsed, this);
34306         r.on("expanded",            this.onRegionExpanded, this);
34307     },
34308
34309     /**
34310      * Performs a layout update.
34311      */
34312     layout : function()
34313     {
34314         if(this.updating) {
34315             return;
34316         }
34317         
34318         // render all the rebions if they have not been done alreayd?
34319         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34320             if(this.regions[region] && !this.regions[region].bodyEl){
34321                 this.regions[region].onRender(this.el)
34322             }
34323         },this);
34324         
34325         var size = this.getViewSize();
34326         var w = size.width;
34327         var h = size.height;
34328         var centerW = w;
34329         var centerH = h;
34330         var centerY = 0;
34331         var centerX = 0;
34332         //var x = 0, y = 0;
34333
34334         var rs = this.regions;
34335         var north = rs["north"];
34336         var south = rs["south"]; 
34337         var west = rs["west"];
34338         var east = rs["east"];
34339         var center = rs["center"];
34340         //if(this.hideOnLayout){ // not supported anymore
34341             //c.el.setStyle("display", "none");
34342         //}
34343         if(north && north.isVisible()){
34344             var b = north.getBox();
34345             var m = north.getMargins();
34346             b.width = w - (m.left+m.right);
34347             b.x = m.left;
34348             b.y = m.top;
34349             centerY = b.height + b.y + m.bottom;
34350             centerH -= centerY;
34351             north.updateBox(this.safeBox(b));
34352         }
34353         if(south && south.isVisible()){
34354             var b = south.getBox();
34355             var m = south.getMargins();
34356             b.width = w - (m.left+m.right);
34357             b.x = m.left;
34358             var totalHeight = (b.height + m.top + m.bottom);
34359             b.y = h - totalHeight + m.top;
34360             centerH -= totalHeight;
34361             south.updateBox(this.safeBox(b));
34362         }
34363         if(west && west.isVisible()){
34364             var b = west.getBox();
34365             var m = west.getMargins();
34366             b.height = centerH - (m.top+m.bottom);
34367             b.x = m.left;
34368             b.y = centerY + m.top;
34369             var totalWidth = (b.width + m.left + m.right);
34370             centerX += totalWidth;
34371             centerW -= totalWidth;
34372             west.updateBox(this.safeBox(b));
34373         }
34374         if(east && east.isVisible()){
34375             var b = east.getBox();
34376             var m = east.getMargins();
34377             b.height = centerH - (m.top+m.bottom);
34378             var totalWidth = (b.width + m.left + m.right);
34379             b.x = w - totalWidth + m.left;
34380             b.y = centerY + m.top;
34381             centerW -= totalWidth;
34382             east.updateBox(this.safeBox(b));
34383         }
34384         if(center){
34385             var m = center.getMargins();
34386             var centerBox = {
34387                 x: centerX + m.left,
34388                 y: centerY + m.top,
34389                 width: centerW - (m.left+m.right),
34390                 height: centerH - (m.top+m.bottom)
34391             };
34392             //if(this.hideOnLayout){
34393                 //center.el.setStyle("display", "block");
34394             //}
34395             center.updateBox(this.safeBox(centerBox));
34396         }
34397         this.el.repaint();
34398         this.fireEvent("layout", this);
34399     },
34400
34401     // private
34402     safeBox : function(box){
34403         box.width = Math.max(0, box.width);
34404         box.height = Math.max(0, box.height);
34405         return box;
34406     },
34407
34408     /**
34409      * Adds a ContentPanel (or subclass) to this layout.
34410      * @param {String} target The target region key (north, south, east, west or center).
34411      * @param {Roo.ContentPanel} panel The panel to add
34412      * @return {Roo.ContentPanel} The added panel
34413      */
34414     add : function(target, panel){
34415          
34416         target = target.toLowerCase();
34417         return this.regions[target].add(panel);
34418     },
34419
34420     /**
34421      * Remove a ContentPanel (or subclass) to this layout.
34422      * @param {String} target The target region key (north, south, east, west or center).
34423      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34424      * @return {Roo.ContentPanel} The removed panel
34425      */
34426     remove : function(target, panel){
34427         target = target.toLowerCase();
34428         return this.regions[target].remove(panel);
34429     },
34430
34431     /**
34432      * Searches all regions for a panel with the specified id
34433      * @param {String} panelId
34434      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34435      */
34436     findPanel : function(panelId){
34437         var rs = this.regions;
34438         for(var target in rs){
34439             if(typeof rs[target] != "function"){
34440                 var p = rs[target].getPanel(panelId);
34441                 if(p){
34442                     return p;
34443                 }
34444             }
34445         }
34446         return null;
34447     },
34448
34449     /**
34450      * Searches all regions for a panel with the specified id and activates (shows) it.
34451      * @param {String/ContentPanel} panelId The panels id or the panel itself
34452      * @return {Roo.ContentPanel} The shown panel or null
34453      */
34454     showPanel : function(panelId) {
34455       var rs = this.regions;
34456       for(var target in rs){
34457          var r = rs[target];
34458          if(typeof r != "function"){
34459             if(r.hasPanel(panelId)){
34460                return r.showPanel(panelId);
34461             }
34462          }
34463       }
34464       return null;
34465    },
34466
34467    /**
34468      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34469      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34470      */
34471    /*
34472     restoreState : function(provider){
34473         if(!provider){
34474             provider = Roo.state.Manager;
34475         }
34476         var sm = new Roo.LayoutStateManager();
34477         sm.init(this, provider);
34478     },
34479 */
34480  
34481  
34482     /**
34483      * Adds a xtype elements to the layout.
34484      * <pre><code>
34485
34486 layout.addxtype({
34487        xtype : 'ContentPanel',
34488        region: 'west',
34489        items: [ .... ]
34490    }
34491 );
34492
34493 layout.addxtype({
34494         xtype : 'NestedLayoutPanel',
34495         region: 'west',
34496         layout: {
34497            center: { },
34498            west: { }   
34499         },
34500         items : [ ... list of content panels or nested layout panels.. ]
34501    }
34502 );
34503 </code></pre>
34504      * @param {Object} cfg Xtype definition of item to add.
34505      */
34506     addxtype : function(cfg)
34507     {
34508         // basically accepts a pannel...
34509         // can accept a layout region..!?!?
34510         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34511         
34512         
34513         // theory?  children can only be panels??
34514         
34515         //if (!cfg.xtype.match(/Panel$/)) {
34516         //    return false;
34517         //}
34518         var ret = false;
34519         
34520         if (typeof(cfg.region) == 'undefined') {
34521             Roo.log("Failed to add Panel, region was not set");
34522             Roo.log(cfg);
34523             return false;
34524         }
34525         var region = cfg.region;
34526         delete cfg.region;
34527         
34528           
34529         var xitems = [];
34530         if (cfg.items) {
34531             xitems = cfg.items;
34532             delete cfg.items;
34533         }
34534         var nb = false;
34535         
34536         switch(cfg.xtype) 
34537         {
34538             case 'Content':  // ContentPanel (el, cfg)
34539             case 'Scroll':  // ContentPanel (el, cfg)
34540             case 'View': 
34541                 cfg.autoCreate = true;
34542                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34543                 //} else {
34544                 //    var el = this.el.createChild();
34545                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34546                 //}
34547                 
34548                 this.add(region, ret);
34549                 break;
34550             
34551             /*
34552             case 'TreePanel': // our new panel!
34553                 cfg.el = this.el.createChild();
34554                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34555                 this.add(region, ret);
34556                 break;
34557             */
34558             
34559             case 'Nest': 
34560                 // create a new Layout (which is  a Border Layout...
34561                 
34562                 var clayout = cfg.layout;
34563                 clayout.el  = this.el.createChild();
34564                 clayout.items   = clayout.items  || [];
34565                 
34566                 delete cfg.layout;
34567                 
34568                 // replace this exitems with the clayout ones..
34569                 xitems = clayout.items;
34570                  
34571                 // force background off if it's in center...
34572                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34573                     cfg.background = false;
34574                 }
34575                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34576                 
34577                 
34578                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34579                 //console.log('adding nested layout panel '  + cfg.toSource());
34580                 this.add(region, ret);
34581                 nb = {}; /// find first...
34582                 break;
34583             
34584             case 'Grid':
34585                 
34586                 // needs grid and region
34587                 
34588                 //var el = this.getRegion(region).el.createChild();
34589                 /*
34590                  *var el = this.el.createChild();
34591                 // create the grid first...
34592                 cfg.grid.container = el;
34593                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34594                 */
34595                 
34596                 if (region == 'center' && this.active ) {
34597                     cfg.background = false;
34598                 }
34599                 
34600                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34601                 
34602                 this.add(region, ret);
34603                 /*
34604                 if (cfg.background) {
34605                     // render grid on panel activation (if panel background)
34606                     ret.on('activate', function(gp) {
34607                         if (!gp.grid.rendered) {
34608                     //        gp.grid.render(el);
34609                         }
34610                     });
34611                 } else {
34612                   //  cfg.grid.render(el);
34613                 }
34614                 */
34615                 break;
34616            
34617            
34618             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34619                 // it was the old xcomponent building that caused this before.
34620                 // espeically if border is the top element in the tree.
34621                 ret = this;
34622                 break; 
34623                 
34624                     
34625                 
34626                 
34627                 
34628             default:
34629                 /*
34630                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34631                     
34632                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34633                     this.add(region, ret);
34634                 } else {
34635                 */
34636                     Roo.log(cfg);
34637                     throw "Can not add '" + cfg.xtype + "' to Border";
34638                     return null;
34639              
34640                                 
34641              
34642         }
34643         this.beginUpdate();
34644         // add children..
34645         var region = '';
34646         var abn = {};
34647         Roo.each(xitems, function(i)  {
34648             region = nb && i.region ? i.region : false;
34649             
34650             var add = ret.addxtype(i);
34651            
34652             if (region) {
34653                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34654                 if (!i.background) {
34655                     abn[region] = nb[region] ;
34656                 }
34657             }
34658             
34659         });
34660         this.endUpdate();
34661
34662         // make the last non-background panel active..
34663         //if (nb) { Roo.log(abn); }
34664         if (nb) {
34665             
34666             for(var r in abn) {
34667                 region = this.getRegion(r);
34668                 if (region) {
34669                     // tried using nb[r], but it does not work..
34670                      
34671                     region.showPanel(abn[r]);
34672                    
34673                 }
34674             }
34675         }
34676         return ret;
34677         
34678     },
34679     
34680     
34681 // private
34682     factory : function(cfg)
34683     {
34684         
34685         var validRegions = Roo.bootstrap.layout.Border.regions;
34686
34687         var target = cfg.region;
34688         cfg.mgr = this;
34689         
34690         var r = Roo.bootstrap.layout;
34691         Roo.log(target);
34692         switch(target){
34693             case "north":
34694                 return new r.North(cfg);
34695             case "south":
34696                 return new r.South(cfg);
34697             case "east":
34698                 return new r.East(cfg);
34699             case "west":
34700                 return new r.West(cfg);
34701             case "center":
34702                 return new r.Center(cfg);
34703         }
34704         throw 'Layout region "'+target+'" not supported.';
34705     }
34706     
34707     
34708 });
34709  /*
34710  * Based on:
34711  * Ext JS Library 1.1.1
34712  * Copyright(c) 2006-2007, Ext JS, LLC.
34713  *
34714  * Originally Released Under LGPL - original licence link has changed is not relivant.
34715  *
34716  * Fork - LGPL
34717  * <script type="text/javascript">
34718  */
34719  
34720 /**
34721  * @class Roo.bootstrap.layout.Basic
34722  * @extends Roo.util.Observable
34723  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34724  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34725  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34726  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34727  * @cfg {string}   region  the region that it inhabits..
34728  * @cfg {bool}   skipConfig skip config?
34729  * 
34730
34731  */
34732 Roo.bootstrap.layout.Basic = function(config){
34733     
34734     this.mgr = config.mgr;
34735     
34736     this.position = config.region;
34737     
34738     var skipConfig = config.skipConfig;
34739     
34740     this.events = {
34741         /**
34742          * @scope Roo.BasicLayoutRegion
34743          */
34744         
34745         /**
34746          * @event beforeremove
34747          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34748          * @param {Roo.LayoutRegion} this
34749          * @param {Roo.ContentPanel} panel The panel
34750          * @param {Object} e The cancel event object
34751          */
34752         "beforeremove" : true,
34753         /**
34754          * @event invalidated
34755          * Fires when the layout for this region is changed.
34756          * @param {Roo.LayoutRegion} this
34757          */
34758         "invalidated" : true,
34759         /**
34760          * @event visibilitychange
34761          * Fires when this region is shown or hidden 
34762          * @param {Roo.LayoutRegion} this
34763          * @param {Boolean} visibility true or false
34764          */
34765         "visibilitychange" : true,
34766         /**
34767          * @event paneladded
34768          * Fires when a panel is added. 
34769          * @param {Roo.LayoutRegion} this
34770          * @param {Roo.ContentPanel} panel The panel
34771          */
34772         "paneladded" : true,
34773         /**
34774          * @event panelremoved
34775          * Fires when a panel is removed. 
34776          * @param {Roo.LayoutRegion} this
34777          * @param {Roo.ContentPanel} panel The panel
34778          */
34779         "panelremoved" : true,
34780         /**
34781          * @event beforecollapse
34782          * Fires when this region before collapse.
34783          * @param {Roo.LayoutRegion} this
34784          */
34785         "beforecollapse" : true,
34786         /**
34787          * @event collapsed
34788          * Fires when this region is collapsed.
34789          * @param {Roo.LayoutRegion} this
34790          */
34791         "collapsed" : true,
34792         /**
34793          * @event expanded
34794          * Fires when this region is expanded.
34795          * @param {Roo.LayoutRegion} this
34796          */
34797         "expanded" : true,
34798         /**
34799          * @event slideshow
34800          * Fires when this region is slid into view.
34801          * @param {Roo.LayoutRegion} this
34802          */
34803         "slideshow" : true,
34804         /**
34805          * @event slidehide
34806          * Fires when this region slides out of view. 
34807          * @param {Roo.LayoutRegion} this
34808          */
34809         "slidehide" : true,
34810         /**
34811          * @event panelactivated
34812          * Fires when a panel is activated. 
34813          * @param {Roo.LayoutRegion} this
34814          * @param {Roo.ContentPanel} panel The activated panel
34815          */
34816         "panelactivated" : true,
34817         /**
34818          * @event resized
34819          * Fires when the user resizes this region. 
34820          * @param {Roo.LayoutRegion} this
34821          * @param {Number} newSize The new size (width for east/west, height for north/south)
34822          */
34823         "resized" : true
34824     };
34825     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34826     this.panels = new Roo.util.MixedCollection();
34827     this.panels.getKey = this.getPanelId.createDelegate(this);
34828     this.box = null;
34829     this.activePanel = null;
34830     // ensure listeners are added...
34831     
34832     if (config.listeners || config.events) {
34833         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34834             listeners : config.listeners || {},
34835             events : config.events || {}
34836         });
34837     }
34838     
34839     if(skipConfig !== true){
34840         this.applyConfig(config);
34841     }
34842 };
34843
34844 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34845 {
34846     getPanelId : function(p){
34847         return p.getId();
34848     },
34849     
34850     applyConfig : function(config){
34851         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34852         this.config = config;
34853         
34854     },
34855     
34856     /**
34857      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34858      * the width, for horizontal (north, south) the height.
34859      * @param {Number} newSize The new width or height
34860      */
34861     resizeTo : function(newSize){
34862         var el = this.el ? this.el :
34863                  (this.activePanel ? this.activePanel.getEl() : null);
34864         if(el){
34865             switch(this.position){
34866                 case "east":
34867                 case "west":
34868                     el.setWidth(newSize);
34869                     this.fireEvent("resized", this, newSize);
34870                 break;
34871                 case "north":
34872                 case "south":
34873                     el.setHeight(newSize);
34874                     this.fireEvent("resized", this, newSize);
34875                 break;                
34876             }
34877         }
34878     },
34879     
34880     getBox : function(){
34881         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34882     },
34883     
34884     getMargins : function(){
34885         return this.margins;
34886     },
34887     
34888     updateBox : function(box){
34889         this.box = box;
34890         var el = this.activePanel.getEl();
34891         el.dom.style.left = box.x + "px";
34892         el.dom.style.top = box.y + "px";
34893         this.activePanel.setSize(box.width, box.height);
34894     },
34895     
34896     /**
34897      * Returns the container element for this region.
34898      * @return {Roo.Element}
34899      */
34900     getEl : function(){
34901         return this.activePanel;
34902     },
34903     
34904     /**
34905      * Returns true if this region is currently visible.
34906      * @return {Boolean}
34907      */
34908     isVisible : function(){
34909         return this.activePanel ? true : false;
34910     },
34911     
34912     setActivePanel : function(panel){
34913         panel = this.getPanel(panel);
34914         if(this.activePanel && this.activePanel != panel){
34915             this.activePanel.setActiveState(false);
34916             this.activePanel.getEl().setLeftTop(-10000,-10000);
34917         }
34918         this.activePanel = panel;
34919         panel.setActiveState(true);
34920         if(this.box){
34921             panel.setSize(this.box.width, this.box.height);
34922         }
34923         this.fireEvent("panelactivated", this, panel);
34924         this.fireEvent("invalidated");
34925     },
34926     
34927     /**
34928      * Show the specified panel.
34929      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34930      * @return {Roo.ContentPanel} The shown panel or null
34931      */
34932     showPanel : function(panel){
34933         panel = this.getPanel(panel);
34934         if(panel){
34935             this.setActivePanel(panel);
34936         }
34937         return panel;
34938     },
34939     
34940     /**
34941      * Get the active panel for this region.
34942      * @return {Roo.ContentPanel} The active panel or null
34943      */
34944     getActivePanel : function(){
34945         return this.activePanel;
34946     },
34947     
34948     /**
34949      * Add the passed ContentPanel(s)
34950      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34951      * @return {Roo.ContentPanel} The panel added (if only one was added)
34952      */
34953     add : function(panel){
34954         if(arguments.length > 1){
34955             for(var i = 0, len = arguments.length; i < len; i++) {
34956                 this.add(arguments[i]);
34957             }
34958             return null;
34959         }
34960         if(this.hasPanel(panel)){
34961             this.showPanel(panel);
34962             return panel;
34963         }
34964         var el = panel.getEl();
34965         if(el.dom.parentNode != this.mgr.el.dom){
34966             this.mgr.el.dom.appendChild(el.dom);
34967         }
34968         if(panel.setRegion){
34969             panel.setRegion(this);
34970         }
34971         this.panels.add(panel);
34972         el.setStyle("position", "absolute");
34973         if(!panel.background){
34974             this.setActivePanel(panel);
34975             if(this.config.initialSize && this.panels.getCount()==1){
34976                 this.resizeTo(this.config.initialSize);
34977             }
34978         }
34979         this.fireEvent("paneladded", this, panel);
34980         return panel;
34981     },
34982     
34983     /**
34984      * Returns true if the panel is in this region.
34985      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34986      * @return {Boolean}
34987      */
34988     hasPanel : function(panel){
34989         if(typeof panel == "object"){ // must be panel obj
34990             panel = panel.getId();
34991         }
34992         return this.getPanel(panel) ? true : false;
34993     },
34994     
34995     /**
34996      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34997      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34998      * @param {Boolean} preservePanel Overrides the config preservePanel option
34999      * @return {Roo.ContentPanel} The panel that was removed
35000      */
35001     remove : function(panel, preservePanel){
35002         panel = this.getPanel(panel);
35003         if(!panel){
35004             return null;
35005         }
35006         var e = {};
35007         this.fireEvent("beforeremove", this, panel, e);
35008         if(e.cancel === true){
35009             return null;
35010         }
35011         var panelId = panel.getId();
35012         this.panels.removeKey(panelId);
35013         return panel;
35014     },
35015     
35016     /**
35017      * Returns the panel specified or null if it's not in this region.
35018      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35019      * @return {Roo.ContentPanel}
35020      */
35021     getPanel : function(id){
35022         if(typeof id == "object"){ // must be panel obj
35023             return id;
35024         }
35025         return this.panels.get(id);
35026     },
35027     
35028     /**
35029      * Returns this regions position (north/south/east/west/center).
35030      * @return {String} 
35031      */
35032     getPosition: function(){
35033         return this.position;    
35034     }
35035 });/*
35036  * Based on:
35037  * Ext JS Library 1.1.1
35038  * Copyright(c) 2006-2007, Ext JS, LLC.
35039  *
35040  * Originally Released Under LGPL - original licence link has changed is not relivant.
35041  *
35042  * Fork - LGPL
35043  * <script type="text/javascript">
35044  */
35045  
35046 /**
35047  * @class Roo.bootstrap.layout.Region
35048  * @extends Roo.bootstrap.layout.Basic
35049  * This class represents a region in a layout manager.
35050  
35051  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35052  * @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})
35053  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35054  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35055  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35056  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35057  * @cfg {String}    title           The title for the region (overrides panel titles)
35058  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35059  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35060  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35061  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35062  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35063  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35064  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35065  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35066  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35067  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35068
35069  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35070  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35071  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35072  * @cfg {Number}    width           For East/West panels
35073  * @cfg {Number}    height          For North/South panels
35074  * @cfg {Boolean}   split           To show the splitter
35075  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35076  * 
35077  * @cfg {string}   cls             Extra CSS classes to add to region
35078  * 
35079  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35080  * @cfg {string}   region  the region that it inhabits..
35081  *
35082
35083  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35084  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35085
35086  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35087  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35088  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35089  */
35090 Roo.bootstrap.layout.Region = function(config)
35091 {
35092     this.applyConfig(config);
35093
35094     var mgr = config.mgr;
35095     var pos = config.region;
35096     config.skipConfig = true;
35097     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35098     
35099     if (mgr.el) {
35100         this.onRender(mgr.el);   
35101     }
35102      
35103     this.visible = true;
35104     this.collapsed = false;
35105     this.unrendered_panels = [];
35106 };
35107
35108 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35109
35110     position: '', // set by wrapper (eg. north/south etc..)
35111     unrendered_panels : null,  // unrendered panels.
35112     createBody : function(){
35113         /** This region's body element 
35114         * @type Roo.Element */
35115         this.bodyEl = this.el.createChild({
35116                 tag: "div",
35117                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35118         });
35119     },
35120
35121     onRender: function(ctr, pos)
35122     {
35123         var dh = Roo.DomHelper;
35124         /** This region's container element 
35125         * @type Roo.Element */
35126         this.el = dh.append(ctr.dom, {
35127                 tag: "div",
35128                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35129             }, true);
35130         /** This region's title element 
35131         * @type Roo.Element */
35132     
35133         this.titleEl = dh.append(this.el.dom,
35134             {
35135                     tag: "div",
35136                     unselectable: "on",
35137                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35138                     children:[
35139                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35140                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35141                     ]}, true);
35142         
35143         this.titleEl.enableDisplayMode();
35144         /** This region's title text element 
35145         * @type HTMLElement */
35146         this.titleTextEl = this.titleEl.dom.firstChild;
35147         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35148         /*
35149         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35150         this.closeBtn.enableDisplayMode();
35151         this.closeBtn.on("click", this.closeClicked, this);
35152         this.closeBtn.hide();
35153     */
35154         this.createBody(this.config);
35155         if(this.config.hideWhenEmpty){
35156             this.hide();
35157             this.on("paneladded", this.validateVisibility, this);
35158             this.on("panelremoved", this.validateVisibility, this);
35159         }
35160         if(this.autoScroll){
35161             this.bodyEl.setStyle("overflow", "auto");
35162         }else{
35163             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35164         }
35165         //if(c.titlebar !== false){
35166             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35167                 this.titleEl.hide();
35168             }else{
35169                 this.titleEl.show();
35170                 if(this.config.title){
35171                     this.titleTextEl.innerHTML = this.config.title;
35172                 }
35173             }
35174         //}
35175         if(this.config.collapsed){
35176             this.collapse(true);
35177         }
35178         if(this.config.hidden){
35179             this.hide();
35180         }
35181         
35182         if (this.unrendered_panels && this.unrendered_panels.length) {
35183             for (var i =0;i< this.unrendered_panels.length; i++) {
35184                 this.add(this.unrendered_panels[i]);
35185             }
35186             this.unrendered_panels = null;
35187             
35188         }
35189         
35190     },
35191     
35192     applyConfig : function(c)
35193     {
35194         /*
35195          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35196             var dh = Roo.DomHelper;
35197             if(c.titlebar !== false){
35198                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35199                 this.collapseBtn.on("click", this.collapse, this);
35200                 this.collapseBtn.enableDisplayMode();
35201                 /*
35202                 if(c.showPin === true || this.showPin){
35203                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35204                     this.stickBtn.enableDisplayMode();
35205                     this.stickBtn.on("click", this.expand, this);
35206                     this.stickBtn.hide();
35207                 }
35208                 
35209             }
35210             */
35211             /** This region's collapsed element
35212             * @type Roo.Element */
35213             /*
35214              *
35215             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35216                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35217             ]}, true);
35218             
35219             if(c.floatable !== false){
35220                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35221                this.collapsedEl.on("click", this.collapseClick, this);
35222             }
35223
35224             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35225                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35226                    id: "message", unselectable: "on", style:{"float":"left"}});
35227                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35228              }
35229             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35230             this.expandBtn.on("click", this.expand, this);
35231             
35232         }
35233         
35234         if(this.collapseBtn){
35235             this.collapseBtn.setVisible(c.collapsible == true);
35236         }
35237         
35238         this.cmargins = c.cmargins || this.cmargins ||
35239                          (this.position == "west" || this.position == "east" ?
35240                              {top: 0, left: 2, right:2, bottom: 0} :
35241                              {top: 2, left: 0, right:0, bottom: 2});
35242         */
35243         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35244         
35245         
35246         this.bottomTabs = c.tabPosition != "top";
35247         
35248         this.autoScroll = c.autoScroll || false;
35249         
35250         
35251        
35252         
35253         this.duration = c.duration || .30;
35254         this.slideDuration = c.slideDuration || .45;
35255         this.config = c;
35256        
35257     },
35258     /**
35259      * Returns true if this region is currently visible.
35260      * @return {Boolean}
35261      */
35262     isVisible : function(){
35263         return this.visible;
35264     },
35265
35266     /**
35267      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35268      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35269      */
35270     //setCollapsedTitle : function(title){
35271     //    title = title || "&#160;";
35272      //   if(this.collapsedTitleTextEl){
35273       //      this.collapsedTitleTextEl.innerHTML = title;
35274        // }
35275     //},
35276
35277     getBox : function(){
35278         var b;
35279       //  if(!this.collapsed){
35280             b = this.el.getBox(false, true);
35281        // }else{
35282           //  b = this.collapsedEl.getBox(false, true);
35283         //}
35284         return b;
35285     },
35286
35287     getMargins : function(){
35288         return this.margins;
35289         //return this.collapsed ? this.cmargins : this.margins;
35290     },
35291 /*
35292     highlight : function(){
35293         this.el.addClass("x-layout-panel-dragover");
35294     },
35295
35296     unhighlight : function(){
35297         this.el.removeClass("x-layout-panel-dragover");
35298     },
35299 */
35300     updateBox : function(box)
35301     {
35302         if (!this.bodyEl) {
35303             return; // not rendered yet..
35304         }
35305         
35306         this.box = box;
35307         if(!this.collapsed){
35308             this.el.dom.style.left = box.x + "px";
35309             this.el.dom.style.top = box.y + "px";
35310             this.updateBody(box.width, box.height);
35311         }else{
35312             this.collapsedEl.dom.style.left = box.x + "px";
35313             this.collapsedEl.dom.style.top = box.y + "px";
35314             this.collapsedEl.setSize(box.width, box.height);
35315         }
35316         if(this.tabs){
35317             this.tabs.autoSizeTabs();
35318         }
35319     },
35320
35321     updateBody : function(w, h)
35322     {
35323         if(w !== null){
35324             this.el.setWidth(w);
35325             w -= this.el.getBorderWidth("rl");
35326             if(this.config.adjustments){
35327                 w += this.config.adjustments[0];
35328             }
35329         }
35330         if(h !== null && h > 0){
35331             this.el.setHeight(h);
35332             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35333             h -= this.el.getBorderWidth("tb");
35334             if(this.config.adjustments){
35335                 h += this.config.adjustments[1];
35336             }
35337             this.bodyEl.setHeight(h);
35338             if(this.tabs){
35339                 h = this.tabs.syncHeight(h);
35340             }
35341         }
35342         if(this.panelSize){
35343             w = w !== null ? w : this.panelSize.width;
35344             h = h !== null ? h : this.panelSize.height;
35345         }
35346         if(this.activePanel){
35347             var el = this.activePanel.getEl();
35348             w = w !== null ? w : el.getWidth();
35349             h = h !== null ? h : el.getHeight();
35350             this.panelSize = {width: w, height: h};
35351             this.activePanel.setSize(w, h);
35352         }
35353         if(Roo.isIE && this.tabs){
35354             this.tabs.el.repaint();
35355         }
35356     },
35357
35358     /**
35359      * Returns the container element for this region.
35360      * @return {Roo.Element}
35361      */
35362     getEl : function(){
35363         return this.el;
35364     },
35365
35366     /**
35367      * Hides this region.
35368      */
35369     hide : function(){
35370         //if(!this.collapsed){
35371             this.el.dom.style.left = "-2000px";
35372             this.el.hide();
35373         //}else{
35374          //   this.collapsedEl.dom.style.left = "-2000px";
35375          //   this.collapsedEl.hide();
35376        // }
35377         this.visible = false;
35378         this.fireEvent("visibilitychange", this, false);
35379     },
35380
35381     /**
35382      * Shows this region if it was previously hidden.
35383      */
35384     show : function(){
35385         //if(!this.collapsed){
35386             this.el.show();
35387         //}else{
35388         //    this.collapsedEl.show();
35389        // }
35390         this.visible = true;
35391         this.fireEvent("visibilitychange", this, true);
35392     },
35393 /*
35394     closeClicked : function(){
35395         if(this.activePanel){
35396             this.remove(this.activePanel);
35397         }
35398     },
35399
35400     collapseClick : function(e){
35401         if(this.isSlid){
35402            e.stopPropagation();
35403            this.slideIn();
35404         }else{
35405            e.stopPropagation();
35406            this.slideOut();
35407         }
35408     },
35409 */
35410     /**
35411      * Collapses this region.
35412      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35413      */
35414     /*
35415     collapse : function(skipAnim, skipCheck = false){
35416         if(this.collapsed) {
35417             return;
35418         }
35419         
35420         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35421             
35422             this.collapsed = true;
35423             if(this.split){
35424                 this.split.el.hide();
35425             }
35426             if(this.config.animate && skipAnim !== true){
35427                 this.fireEvent("invalidated", this);
35428                 this.animateCollapse();
35429             }else{
35430                 this.el.setLocation(-20000,-20000);
35431                 this.el.hide();
35432                 this.collapsedEl.show();
35433                 this.fireEvent("collapsed", this);
35434                 this.fireEvent("invalidated", this);
35435             }
35436         }
35437         
35438     },
35439 */
35440     animateCollapse : function(){
35441         // overridden
35442     },
35443
35444     /**
35445      * Expands this region if it was previously collapsed.
35446      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35447      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35448      */
35449     /*
35450     expand : function(e, skipAnim){
35451         if(e) {
35452             e.stopPropagation();
35453         }
35454         if(!this.collapsed || this.el.hasActiveFx()) {
35455             return;
35456         }
35457         if(this.isSlid){
35458             this.afterSlideIn();
35459             skipAnim = true;
35460         }
35461         this.collapsed = false;
35462         if(this.config.animate && skipAnim !== true){
35463             this.animateExpand();
35464         }else{
35465             this.el.show();
35466             if(this.split){
35467                 this.split.el.show();
35468             }
35469             this.collapsedEl.setLocation(-2000,-2000);
35470             this.collapsedEl.hide();
35471             this.fireEvent("invalidated", this);
35472             this.fireEvent("expanded", this);
35473         }
35474     },
35475 */
35476     animateExpand : function(){
35477         // overridden
35478     },
35479
35480     initTabs : function()
35481     {
35482         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35483         
35484         var ts = new Roo.bootstrap.panel.Tabs({
35485                 el: this.bodyEl.dom,
35486                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35487                 disableTooltips: this.config.disableTabTips,
35488                 toolbar : this.config.toolbar
35489             });
35490         
35491         if(this.config.hideTabs){
35492             ts.stripWrap.setDisplayed(false);
35493         }
35494         this.tabs = ts;
35495         ts.resizeTabs = this.config.resizeTabs === true;
35496         ts.minTabWidth = this.config.minTabWidth || 40;
35497         ts.maxTabWidth = this.config.maxTabWidth || 250;
35498         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35499         ts.monitorResize = false;
35500         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35501         ts.bodyEl.addClass('roo-layout-tabs-body');
35502         this.panels.each(this.initPanelAsTab, this);
35503     },
35504
35505     initPanelAsTab : function(panel){
35506         var ti = this.tabs.addTab(
35507             panel.getEl().id,
35508             panel.getTitle(),
35509             null,
35510             this.config.closeOnTab && panel.isClosable(),
35511             panel.tpl
35512         );
35513         if(panel.tabTip !== undefined){
35514             ti.setTooltip(panel.tabTip);
35515         }
35516         ti.on("activate", function(){
35517               this.setActivePanel(panel);
35518         }, this);
35519         
35520         if(this.config.closeOnTab){
35521             ti.on("beforeclose", function(t, e){
35522                 e.cancel = true;
35523                 this.remove(panel);
35524             }, this);
35525         }
35526         
35527         panel.tabItem = ti;
35528         
35529         return ti;
35530     },
35531
35532     updatePanelTitle : function(panel, title)
35533     {
35534         if(this.activePanel == panel){
35535             this.updateTitle(title);
35536         }
35537         if(this.tabs){
35538             var ti = this.tabs.getTab(panel.getEl().id);
35539             ti.setText(title);
35540             if(panel.tabTip !== undefined){
35541                 ti.setTooltip(panel.tabTip);
35542             }
35543         }
35544     },
35545
35546     updateTitle : function(title){
35547         if(this.titleTextEl && !this.config.title){
35548             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35549         }
35550     },
35551
35552     setActivePanel : function(panel)
35553     {
35554         panel = this.getPanel(panel);
35555         if(this.activePanel && this.activePanel != panel){
35556             if(this.activePanel.setActiveState(false) === false){
35557                 return;
35558             }
35559         }
35560         this.activePanel = panel;
35561         panel.setActiveState(true);
35562         if(this.panelSize){
35563             panel.setSize(this.panelSize.width, this.panelSize.height);
35564         }
35565         if(this.closeBtn){
35566             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35567         }
35568         this.updateTitle(panel.getTitle());
35569         if(this.tabs){
35570             this.fireEvent("invalidated", this);
35571         }
35572         this.fireEvent("panelactivated", this, panel);
35573     },
35574
35575     /**
35576      * Shows the specified panel.
35577      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35578      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35579      */
35580     showPanel : function(panel)
35581     {
35582         panel = this.getPanel(panel);
35583         if(panel){
35584             if(this.tabs){
35585                 var tab = this.tabs.getTab(panel.getEl().id);
35586                 if(tab.isHidden()){
35587                     this.tabs.unhideTab(tab.id);
35588                 }
35589                 tab.activate();
35590             }else{
35591                 this.setActivePanel(panel);
35592             }
35593         }
35594         return panel;
35595     },
35596
35597     /**
35598      * Get the active panel for this region.
35599      * @return {Roo.ContentPanel} The active panel or null
35600      */
35601     getActivePanel : function(){
35602         return this.activePanel;
35603     },
35604
35605     validateVisibility : function(){
35606         if(this.panels.getCount() < 1){
35607             this.updateTitle("&#160;");
35608             this.closeBtn.hide();
35609             this.hide();
35610         }else{
35611             if(!this.isVisible()){
35612                 this.show();
35613             }
35614         }
35615     },
35616
35617     /**
35618      * Adds the passed ContentPanel(s) to this region.
35619      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35620      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35621      */
35622     add : function(panel)
35623     {
35624         if(arguments.length > 1){
35625             for(var i = 0, len = arguments.length; i < len; i++) {
35626                 this.add(arguments[i]);
35627             }
35628             return null;
35629         }
35630         
35631         // if we have not been rendered yet, then we can not really do much of this..
35632         if (!this.bodyEl) {
35633             this.unrendered_panels.push(panel);
35634             return panel;
35635         }
35636         
35637         
35638         
35639         
35640         if(this.hasPanel(panel)){
35641             this.showPanel(panel);
35642             return panel;
35643         }
35644         panel.setRegion(this);
35645         this.panels.add(panel);
35646        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35647             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35648             // and hide them... ???
35649             this.bodyEl.dom.appendChild(panel.getEl().dom);
35650             if(panel.background !== true){
35651                 this.setActivePanel(panel);
35652             }
35653             this.fireEvent("paneladded", this, panel);
35654             return panel;
35655         }
35656         */
35657         if(!this.tabs){
35658             this.initTabs();
35659         }else{
35660             this.initPanelAsTab(panel);
35661         }
35662         
35663         
35664         if(panel.background !== true){
35665             this.tabs.activate(panel.getEl().id);
35666         }
35667         this.fireEvent("paneladded", this, panel);
35668         return panel;
35669     },
35670
35671     /**
35672      * Hides the tab for the specified panel.
35673      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35674      */
35675     hidePanel : function(panel){
35676         if(this.tabs && (panel = this.getPanel(panel))){
35677             this.tabs.hideTab(panel.getEl().id);
35678         }
35679     },
35680
35681     /**
35682      * Unhides the tab for a previously hidden panel.
35683      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35684      */
35685     unhidePanel : function(panel){
35686         if(this.tabs && (panel = this.getPanel(panel))){
35687             this.tabs.unhideTab(panel.getEl().id);
35688         }
35689     },
35690
35691     clearPanels : function(){
35692         while(this.panels.getCount() > 0){
35693              this.remove(this.panels.first());
35694         }
35695     },
35696
35697     /**
35698      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35699      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35700      * @param {Boolean} preservePanel Overrides the config preservePanel option
35701      * @return {Roo.ContentPanel} The panel that was removed
35702      */
35703     remove : function(panel, preservePanel)
35704     {
35705         panel = this.getPanel(panel);
35706         if(!panel){
35707             return null;
35708         }
35709         var e = {};
35710         this.fireEvent("beforeremove", this, panel, e);
35711         if(e.cancel === true){
35712             return null;
35713         }
35714         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35715         var panelId = panel.getId();
35716         this.panels.removeKey(panelId);
35717         if(preservePanel){
35718             document.body.appendChild(panel.getEl().dom);
35719         }
35720         if(this.tabs){
35721             this.tabs.removeTab(panel.getEl().id);
35722         }else if (!preservePanel){
35723             this.bodyEl.dom.removeChild(panel.getEl().dom);
35724         }
35725         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35726             var p = this.panels.first();
35727             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35728             tempEl.appendChild(p.getEl().dom);
35729             this.bodyEl.update("");
35730             this.bodyEl.dom.appendChild(p.getEl().dom);
35731             tempEl = null;
35732             this.updateTitle(p.getTitle());
35733             this.tabs = null;
35734             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35735             this.setActivePanel(p);
35736         }
35737         panel.setRegion(null);
35738         if(this.activePanel == panel){
35739             this.activePanel = null;
35740         }
35741         if(this.config.autoDestroy !== false && preservePanel !== true){
35742             try{panel.destroy();}catch(e){}
35743         }
35744         this.fireEvent("panelremoved", this, panel);
35745         return panel;
35746     },
35747
35748     /**
35749      * Returns the TabPanel component used by this region
35750      * @return {Roo.TabPanel}
35751      */
35752     getTabs : function(){
35753         return this.tabs;
35754     },
35755
35756     createTool : function(parentEl, className){
35757         var btn = Roo.DomHelper.append(parentEl, {
35758             tag: "div",
35759             cls: "x-layout-tools-button",
35760             children: [ {
35761                 tag: "div",
35762                 cls: "roo-layout-tools-button-inner " + className,
35763                 html: "&#160;"
35764             }]
35765         }, true);
35766         btn.addClassOnOver("roo-layout-tools-button-over");
35767         return btn;
35768     }
35769 });/*
35770  * Based on:
35771  * Ext JS Library 1.1.1
35772  * Copyright(c) 2006-2007, Ext JS, LLC.
35773  *
35774  * Originally Released Under LGPL - original licence link has changed is not relivant.
35775  *
35776  * Fork - LGPL
35777  * <script type="text/javascript">
35778  */
35779  
35780
35781
35782 /**
35783  * @class Roo.SplitLayoutRegion
35784  * @extends Roo.LayoutRegion
35785  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35786  */
35787 Roo.bootstrap.layout.Split = function(config){
35788     this.cursor = config.cursor;
35789     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35790 };
35791
35792 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35793 {
35794     splitTip : "Drag to resize.",
35795     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35796     useSplitTips : false,
35797
35798     applyConfig : function(config){
35799         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35800     },
35801     
35802     onRender : function(ctr,pos) {
35803         
35804         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35805         if(!this.config.split){
35806             return;
35807         }
35808         if(!this.split){
35809             
35810             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35811                             tag: "div",
35812                             id: this.el.id + "-split",
35813                             cls: "roo-layout-split roo-layout-split-"+this.position,
35814                             html: "&#160;"
35815             });
35816             /** The SplitBar for this region 
35817             * @type Roo.SplitBar */
35818             // does not exist yet...
35819             Roo.log([this.position, this.orientation]);
35820             
35821             this.split = new Roo.bootstrap.SplitBar({
35822                 dragElement : splitEl,
35823                 resizingElement: this.el,
35824                 orientation : this.orientation
35825             });
35826             
35827             this.split.on("moved", this.onSplitMove, this);
35828             this.split.useShim = this.config.useShim === true;
35829             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35830             if(this.useSplitTips){
35831                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35832             }
35833             //if(config.collapsible){
35834             //    this.split.el.on("dblclick", this.collapse,  this);
35835             //}
35836         }
35837         if(typeof this.config.minSize != "undefined"){
35838             this.split.minSize = this.config.minSize;
35839         }
35840         if(typeof this.config.maxSize != "undefined"){
35841             this.split.maxSize = this.config.maxSize;
35842         }
35843         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35844             this.hideSplitter();
35845         }
35846         
35847     },
35848
35849     getHMaxSize : function(){
35850          var cmax = this.config.maxSize || 10000;
35851          var center = this.mgr.getRegion("center");
35852          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35853     },
35854
35855     getVMaxSize : function(){
35856          var cmax = this.config.maxSize || 10000;
35857          var center = this.mgr.getRegion("center");
35858          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35859     },
35860
35861     onSplitMove : function(split, newSize){
35862         this.fireEvent("resized", this, newSize);
35863     },
35864     
35865     /** 
35866      * Returns the {@link Roo.SplitBar} for this region.
35867      * @return {Roo.SplitBar}
35868      */
35869     getSplitBar : function(){
35870         return this.split;
35871     },
35872     
35873     hide : function(){
35874         this.hideSplitter();
35875         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35876     },
35877
35878     hideSplitter : function(){
35879         if(this.split){
35880             this.split.el.setLocation(-2000,-2000);
35881             this.split.el.hide();
35882         }
35883     },
35884
35885     show : function(){
35886         if(this.split){
35887             this.split.el.show();
35888         }
35889         Roo.bootstrap.layout.Split.superclass.show.call(this);
35890     },
35891     
35892     beforeSlide: function(){
35893         if(Roo.isGecko){// firefox overflow auto bug workaround
35894             this.bodyEl.clip();
35895             if(this.tabs) {
35896                 this.tabs.bodyEl.clip();
35897             }
35898             if(this.activePanel){
35899                 this.activePanel.getEl().clip();
35900                 
35901                 if(this.activePanel.beforeSlide){
35902                     this.activePanel.beforeSlide();
35903                 }
35904             }
35905         }
35906     },
35907     
35908     afterSlide : function(){
35909         if(Roo.isGecko){// firefox overflow auto bug workaround
35910             this.bodyEl.unclip();
35911             if(this.tabs) {
35912                 this.tabs.bodyEl.unclip();
35913             }
35914             if(this.activePanel){
35915                 this.activePanel.getEl().unclip();
35916                 if(this.activePanel.afterSlide){
35917                     this.activePanel.afterSlide();
35918                 }
35919             }
35920         }
35921     },
35922
35923     initAutoHide : function(){
35924         if(this.autoHide !== false){
35925             if(!this.autoHideHd){
35926                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35927                 this.autoHideHd = {
35928                     "mouseout": function(e){
35929                         if(!e.within(this.el, true)){
35930                             st.delay(500);
35931                         }
35932                     },
35933                     "mouseover" : function(e){
35934                         st.cancel();
35935                     },
35936                     scope : this
35937                 };
35938             }
35939             this.el.on(this.autoHideHd);
35940         }
35941     },
35942
35943     clearAutoHide : function(){
35944         if(this.autoHide !== false){
35945             this.el.un("mouseout", this.autoHideHd.mouseout);
35946             this.el.un("mouseover", this.autoHideHd.mouseover);
35947         }
35948     },
35949
35950     clearMonitor : function(){
35951         Roo.get(document).un("click", this.slideInIf, this);
35952     },
35953
35954     // these names are backwards but not changed for compat
35955     slideOut : function(){
35956         if(this.isSlid || this.el.hasActiveFx()){
35957             return;
35958         }
35959         this.isSlid = true;
35960         if(this.collapseBtn){
35961             this.collapseBtn.hide();
35962         }
35963         this.closeBtnState = this.closeBtn.getStyle('display');
35964         this.closeBtn.hide();
35965         if(this.stickBtn){
35966             this.stickBtn.show();
35967         }
35968         this.el.show();
35969         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35970         this.beforeSlide();
35971         this.el.setStyle("z-index", 10001);
35972         this.el.slideIn(this.getSlideAnchor(), {
35973             callback: function(){
35974                 this.afterSlide();
35975                 this.initAutoHide();
35976                 Roo.get(document).on("click", this.slideInIf, this);
35977                 this.fireEvent("slideshow", this);
35978             },
35979             scope: this,
35980             block: true
35981         });
35982     },
35983
35984     afterSlideIn : function(){
35985         this.clearAutoHide();
35986         this.isSlid = false;
35987         this.clearMonitor();
35988         this.el.setStyle("z-index", "");
35989         if(this.collapseBtn){
35990             this.collapseBtn.show();
35991         }
35992         this.closeBtn.setStyle('display', this.closeBtnState);
35993         if(this.stickBtn){
35994             this.stickBtn.hide();
35995         }
35996         this.fireEvent("slidehide", this);
35997     },
35998
35999     slideIn : function(cb){
36000         if(!this.isSlid || this.el.hasActiveFx()){
36001             Roo.callback(cb);
36002             return;
36003         }
36004         this.isSlid = false;
36005         this.beforeSlide();
36006         this.el.slideOut(this.getSlideAnchor(), {
36007             callback: function(){
36008                 this.el.setLeftTop(-10000, -10000);
36009                 this.afterSlide();
36010                 this.afterSlideIn();
36011                 Roo.callback(cb);
36012             },
36013             scope: this,
36014             block: true
36015         });
36016     },
36017     
36018     slideInIf : function(e){
36019         if(!e.within(this.el)){
36020             this.slideIn();
36021         }
36022     },
36023
36024     animateCollapse : function(){
36025         this.beforeSlide();
36026         this.el.setStyle("z-index", 20000);
36027         var anchor = this.getSlideAnchor();
36028         this.el.slideOut(anchor, {
36029             callback : function(){
36030                 this.el.setStyle("z-index", "");
36031                 this.collapsedEl.slideIn(anchor, {duration:.3});
36032                 this.afterSlide();
36033                 this.el.setLocation(-10000,-10000);
36034                 this.el.hide();
36035                 this.fireEvent("collapsed", this);
36036             },
36037             scope: this,
36038             block: true
36039         });
36040     },
36041
36042     animateExpand : function(){
36043         this.beforeSlide();
36044         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36045         this.el.setStyle("z-index", 20000);
36046         this.collapsedEl.hide({
36047             duration:.1
36048         });
36049         this.el.slideIn(this.getSlideAnchor(), {
36050             callback : function(){
36051                 this.el.setStyle("z-index", "");
36052                 this.afterSlide();
36053                 if(this.split){
36054                     this.split.el.show();
36055                 }
36056                 this.fireEvent("invalidated", this);
36057                 this.fireEvent("expanded", this);
36058             },
36059             scope: this,
36060             block: true
36061         });
36062     },
36063
36064     anchors : {
36065         "west" : "left",
36066         "east" : "right",
36067         "north" : "top",
36068         "south" : "bottom"
36069     },
36070
36071     sanchors : {
36072         "west" : "l",
36073         "east" : "r",
36074         "north" : "t",
36075         "south" : "b"
36076     },
36077
36078     canchors : {
36079         "west" : "tl-tr",
36080         "east" : "tr-tl",
36081         "north" : "tl-bl",
36082         "south" : "bl-tl"
36083     },
36084
36085     getAnchor : function(){
36086         return this.anchors[this.position];
36087     },
36088
36089     getCollapseAnchor : function(){
36090         return this.canchors[this.position];
36091     },
36092
36093     getSlideAnchor : function(){
36094         return this.sanchors[this.position];
36095     },
36096
36097     getAlignAdj : function(){
36098         var cm = this.cmargins;
36099         switch(this.position){
36100             case "west":
36101                 return [0, 0];
36102             break;
36103             case "east":
36104                 return [0, 0];
36105             break;
36106             case "north":
36107                 return [0, 0];
36108             break;
36109             case "south":
36110                 return [0, 0];
36111             break;
36112         }
36113     },
36114
36115     getExpandAdj : function(){
36116         var c = this.collapsedEl, cm = this.cmargins;
36117         switch(this.position){
36118             case "west":
36119                 return [-(cm.right+c.getWidth()+cm.left), 0];
36120             break;
36121             case "east":
36122                 return [cm.right+c.getWidth()+cm.left, 0];
36123             break;
36124             case "north":
36125                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36126             break;
36127             case "south":
36128                 return [0, cm.top+cm.bottom+c.getHeight()];
36129             break;
36130         }
36131     }
36132 });/*
36133  * Based on:
36134  * Ext JS Library 1.1.1
36135  * Copyright(c) 2006-2007, Ext JS, LLC.
36136  *
36137  * Originally Released Under LGPL - original licence link has changed is not relivant.
36138  *
36139  * Fork - LGPL
36140  * <script type="text/javascript">
36141  */
36142 /*
36143  * These classes are private internal classes
36144  */
36145 Roo.bootstrap.layout.Center = function(config){
36146     config.region = "center";
36147     Roo.bootstrap.layout.Region.call(this, config);
36148     this.visible = true;
36149     this.minWidth = config.minWidth || 20;
36150     this.minHeight = config.minHeight || 20;
36151 };
36152
36153 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36154     hide : function(){
36155         // center panel can't be hidden
36156     },
36157     
36158     show : function(){
36159         // center panel can't be hidden
36160     },
36161     
36162     getMinWidth: function(){
36163         return this.minWidth;
36164     },
36165     
36166     getMinHeight: function(){
36167         return this.minHeight;
36168     }
36169 });
36170
36171
36172
36173
36174  
36175
36176
36177
36178
36179
36180 Roo.bootstrap.layout.North = function(config)
36181 {
36182     config.region = 'north';
36183     config.cursor = 'n-resize';
36184     
36185     Roo.bootstrap.layout.Split.call(this, config);
36186     
36187     
36188     if(this.split){
36189         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36190         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36191         this.split.el.addClass("roo-layout-split-v");
36192     }
36193     var size = config.initialSize || config.height;
36194     if(typeof size != "undefined"){
36195         this.el.setHeight(size);
36196     }
36197 };
36198 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36199 {
36200     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36201     
36202     
36203     
36204     getBox : function(){
36205         if(this.collapsed){
36206             return this.collapsedEl.getBox();
36207         }
36208         var box = this.el.getBox();
36209         if(this.split){
36210             box.height += this.split.el.getHeight();
36211         }
36212         return box;
36213     },
36214     
36215     updateBox : function(box){
36216         if(this.split && !this.collapsed){
36217             box.height -= this.split.el.getHeight();
36218             this.split.el.setLeft(box.x);
36219             this.split.el.setTop(box.y+box.height);
36220             this.split.el.setWidth(box.width);
36221         }
36222         if(this.collapsed){
36223             this.updateBody(box.width, null);
36224         }
36225         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36226     }
36227 });
36228
36229
36230
36231
36232
36233 Roo.bootstrap.layout.South = function(config){
36234     config.region = 'south';
36235     config.cursor = 's-resize';
36236     Roo.bootstrap.layout.Split.call(this, config);
36237     if(this.split){
36238         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36239         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36240         this.split.el.addClass("roo-layout-split-v");
36241     }
36242     var size = config.initialSize || config.height;
36243     if(typeof size != "undefined"){
36244         this.el.setHeight(size);
36245     }
36246 };
36247
36248 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36249     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36250     getBox : function(){
36251         if(this.collapsed){
36252             return this.collapsedEl.getBox();
36253         }
36254         var box = this.el.getBox();
36255         if(this.split){
36256             var sh = this.split.el.getHeight();
36257             box.height += sh;
36258             box.y -= sh;
36259         }
36260         return box;
36261     },
36262     
36263     updateBox : function(box){
36264         if(this.split && !this.collapsed){
36265             var sh = this.split.el.getHeight();
36266             box.height -= sh;
36267             box.y += sh;
36268             this.split.el.setLeft(box.x);
36269             this.split.el.setTop(box.y-sh);
36270             this.split.el.setWidth(box.width);
36271         }
36272         if(this.collapsed){
36273             this.updateBody(box.width, null);
36274         }
36275         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36276     }
36277 });
36278
36279 Roo.bootstrap.layout.East = function(config){
36280     config.region = "east";
36281     config.cursor = "e-resize";
36282     Roo.bootstrap.layout.Split.call(this, config);
36283     if(this.split){
36284         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36285         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36286         this.split.el.addClass("roo-layout-split-h");
36287     }
36288     var size = config.initialSize || config.width;
36289     if(typeof size != "undefined"){
36290         this.el.setWidth(size);
36291     }
36292 };
36293 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36294     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36295     getBox : function(){
36296         if(this.collapsed){
36297             return this.collapsedEl.getBox();
36298         }
36299         var box = this.el.getBox();
36300         if(this.split){
36301             var sw = this.split.el.getWidth();
36302             box.width += sw;
36303             box.x -= sw;
36304         }
36305         return box;
36306     },
36307
36308     updateBox : function(box){
36309         if(this.split && !this.collapsed){
36310             var sw = this.split.el.getWidth();
36311             box.width -= sw;
36312             this.split.el.setLeft(box.x);
36313             this.split.el.setTop(box.y);
36314             this.split.el.setHeight(box.height);
36315             box.x += sw;
36316         }
36317         if(this.collapsed){
36318             this.updateBody(null, box.height);
36319         }
36320         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36321     }
36322 });
36323
36324 Roo.bootstrap.layout.West = function(config){
36325     config.region = "west";
36326     config.cursor = "w-resize";
36327     
36328     Roo.bootstrap.layout.Split.call(this, config);
36329     if(this.split){
36330         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36331         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36332         this.split.el.addClass("roo-layout-split-h");
36333     }
36334     
36335 };
36336 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36337     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36338     
36339     onRender: function(ctr, pos)
36340     {
36341         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36342         var size = this.config.initialSize || this.config.width;
36343         if(typeof size != "undefined"){
36344             this.el.setWidth(size);
36345         }
36346     },
36347     
36348     getBox : function(){
36349         if(this.collapsed){
36350             return this.collapsedEl.getBox();
36351         }
36352         var box = this.el.getBox();
36353         if(this.split){
36354             box.width += this.split.el.getWidth();
36355         }
36356         return box;
36357     },
36358     
36359     updateBox : function(box){
36360         if(this.split && !this.collapsed){
36361             var sw = this.split.el.getWidth();
36362             box.width -= sw;
36363             this.split.el.setLeft(box.x+box.width);
36364             this.split.el.setTop(box.y);
36365             this.split.el.setHeight(box.height);
36366         }
36367         if(this.collapsed){
36368             this.updateBody(null, box.height);
36369         }
36370         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36371     }
36372 });
36373 Roo.namespace("Roo.bootstrap.panel");/*
36374  * Based on:
36375  * Ext JS Library 1.1.1
36376  * Copyright(c) 2006-2007, Ext JS, LLC.
36377  *
36378  * Originally Released Under LGPL - original licence link has changed is not relivant.
36379  *
36380  * Fork - LGPL
36381  * <script type="text/javascript">
36382  */
36383 /**
36384  * @class Roo.ContentPanel
36385  * @extends Roo.util.Observable
36386  * A basic ContentPanel element.
36387  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36388  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36389  * @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
36390  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36391  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36392  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36393  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36394  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36395  * @cfg {String} title          The title for this panel
36396  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36397  * @cfg {String} url            Calls {@link #setUrl} with this value
36398  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36399  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36400  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36401  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36402  * @cfg {Boolean} badges render the badges
36403
36404  * @constructor
36405  * Create a new ContentPanel.
36406  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36407  * @param {String/Object} config A string to set only the title or a config object
36408  * @param {String} content (optional) Set the HTML content for this panel
36409  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36410  */
36411 Roo.bootstrap.panel.Content = function( config){
36412     
36413     this.tpl = config.tpl || false;
36414     
36415     var el = config.el;
36416     var content = config.content;
36417
36418     if(config.autoCreate){ // xtype is available if this is called from factory
36419         el = Roo.id();
36420     }
36421     this.el = Roo.get(el);
36422     if(!this.el && config && config.autoCreate){
36423         if(typeof config.autoCreate == "object"){
36424             if(!config.autoCreate.id){
36425                 config.autoCreate.id = config.id||el;
36426             }
36427             this.el = Roo.DomHelper.append(document.body,
36428                         config.autoCreate, true);
36429         }else{
36430             var elcfg =  {   tag: "div",
36431                             cls: "roo-layout-inactive-content",
36432                             id: config.id||el
36433                             };
36434             if (config.html) {
36435                 elcfg.html = config.html;
36436                 
36437             }
36438                         
36439             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36440         }
36441     } 
36442     this.closable = false;
36443     this.loaded = false;
36444     this.active = false;
36445    
36446       
36447     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36448         
36449         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36450         
36451         this.wrapEl = this.el; //this.el.wrap();
36452         var ti = [];
36453         if (config.toolbar.items) {
36454             ti = config.toolbar.items ;
36455             delete config.toolbar.items ;
36456         }
36457         
36458         var nitems = [];
36459         this.toolbar.render(this.wrapEl, 'before');
36460         for(var i =0;i < ti.length;i++) {
36461           //  Roo.log(['add child', items[i]]);
36462             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36463         }
36464         this.toolbar.items = nitems;
36465         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36466         delete config.toolbar;
36467         
36468     }
36469     /*
36470     // xtype created footer. - not sure if will work as we normally have to render first..
36471     if (this.footer && !this.footer.el && this.footer.xtype) {
36472         if (!this.wrapEl) {
36473             this.wrapEl = this.el.wrap();
36474         }
36475     
36476         this.footer.container = this.wrapEl.createChild();
36477          
36478         this.footer = Roo.factory(this.footer, Roo);
36479         
36480     }
36481     */
36482     
36483      if(typeof config == "string"){
36484         this.title = config;
36485     }else{
36486         Roo.apply(this, config);
36487     }
36488     
36489     if(this.resizeEl){
36490         this.resizeEl = Roo.get(this.resizeEl, true);
36491     }else{
36492         this.resizeEl = this.el;
36493     }
36494     // handle view.xtype
36495     
36496  
36497     
36498     
36499     this.addEvents({
36500         /**
36501          * @event activate
36502          * Fires when this panel is activated. 
36503          * @param {Roo.ContentPanel} this
36504          */
36505         "activate" : true,
36506         /**
36507          * @event deactivate
36508          * Fires when this panel is activated. 
36509          * @param {Roo.ContentPanel} this
36510          */
36511         "deactivate" : true,
36512
36513         /**
36514          * @event resize
36515          * Fires when this panel is resized if fitToFrame is true.
36516          * @param {Roo.ContentPanel} this
36517          * @param {Number} width The width after any component adjustments
36518          * @param {Number} height The height after any component adjustments
36519          */
36520         "resize" : true,
36521         
36522          /**
36523          * @event render
36524          * Fires when this tab is created
36525          * @param {Roo.ContentPanel} this
36526          */
36527         "render" : true
36528         
36529         
36530         
36531     });
36532     
36533
36534     
36535     
36536     if(this.autoScroll){
36537         this.resizeEl.setStyle("overflow", "auto");
36538     } else {
36539         // fix randome scrolling
36540         //this.el.on('scroll', function() {
36541         //    Roo.log('fix random scolling');
36542         //    this.scrollTo('top',0); 
36543         //});
36544     }
36545     content = content || this.content;
36546     if(content){
36547         this.setContent(content);
36548     }
36549     if(config && config.url){
36550         this.setUrl(this.url, this.params, this.loadOnce);
36551     }
36552     
36553     
36554     
36555     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36556     
36557     if (this.view && typeof(this.view.xtype) != 'undefined') {
36558         this.view.el = this.el.appendChild(document.createElement("div"));
36559         this.view = Roo.factory(this.view); 
36560         this.view.render  &&  this.view.render(false, '');  
36561     }
36562     
36563     
36564     this.fireEvent('render', this);
36565 };
36566
36567 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36568     
36569     tabTip : '',
36570     
36571     setRegion : function(region){
36572         this.region = region;
36573         this.setActiveClass(region && !this.background);
36574     },
36575     
36576     
36577     setActiveClass: function(state)
36578     {
36579         if(state){
36580            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36581            this.el.setStyle('position','relative');
36582         }else{
36583            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36584            this.el.setStyle('position', 'absolute');
36585         } 
36586     },
36587     
36588     /**
36589      * Returns the toolbar for this Panel if one was configured. 
36590      * @return {Roo.Toolbar} 
36591      */
36592     getToolbar : function(){
36593         return this.toolbar;
36594     },
36595     
36596     setActiveState : function(active)
36597     {
36598         this.active = active;
36599         this.setActiveClass(active);
36600         if(!active){
36601             if(this.fireEvent("deactivate", this) === false){
36602                 return false;
36603             }
36604             return true;
36605         }
36606         this.fireEvent("activate", this);
36607         return true;
36608     },
36609     /**
36610      * Updates this panel's element
36611      * @param {String} content The new content
36612      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36613     */
36614     setContent : function(content, loadScripts){
36615         this.el.update(content, loadScripts);
36616     },
36617
36618     ignoreResize : function(w, h){
36619         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36620             return true;
36621         }else{
36622             this.lastSize = {width: w, height: h};
36623             return false;
36624         }
36625     },
36626     /**
36627      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36628      * @return {Roo.UpdateManager} The UpdateManager
36629      */
36630     getUpdateManager : function(){
36631         return this.el.getUpdateManager();
36632     },
36633      /**
36634      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36635      * @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:
36636 <pre><code>
36637 panel.load({
36638     url: "your-url.php",
36639     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36640     callback: yourFunction,
36641     scope: yourObject, //(optional scope)
36642     discardUrl: false,
36643     nocache: false,
36644     text: "Loading...",
36645     timeout: 30,
36646     scripts: false
36647 });
36648 </code></pre>
36649      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36650      * 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.
36651      * @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}
36652      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36653      * @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.
36654      * @return {Roo.ContentPanel} this
36655      */
36656     load : function(){
36657         var um = this.el.getUpdateManager();
36658         um.update.apply(um, arguments);
36659         return this;
36660     },
36661
36662
36663     /**
36664      * 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.
36665      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36666      * @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)
36667      * @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)
36668      * @return {Roo.UpdateManager} The UpdateManager
36669      */
36670     setUrl : function(url, params, loadOnce){
36671         if(this.refreshDelegate){
36672             this.removeListener("activate", this.refreshDelegate);
36673         }
36674         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36675         this.on("activate", this.refreshDelegate);
36676         return this.el.getUpdateManager();
36677     },
36678     
36679     _handleRefresh : function(url, params, loadOnce){
36680         if(!loadOnce || !this.loaded){
36681             var updater = this.el.getUpdateManager();
36682             updater.update(url, params, this._setLoaded.createDelegate(this));
36683         }
36684     },
36685     
36686     _setLoaded : function(){
36687         this.loaded = true;
36688     }, 
36689     
36690     /**
36691      * Returns this panel's id
36692      * @return {String} 
36693      */
36694     getId : function(){
36695         return this.el.id;
36696     },
36697     
36698     /** 
36699      * Returns this panel's element - used by regiosn to add.
36700      * @return {Roo.Element} 
36701      */
36702     getEl : function(){
36703         return this.wrapEl || this.el;
36704     },
36705     
36706    
36707     
36708     adjustForComponents : function(width, height)
36709     {
36710         //Roo.log('adjustForComponents ');
36711         if(this.resizeEl != this.el){
36712             width -= this.el.getFrameWidth('lr');
36713             height -= this.el.getFrameWidth('tb');
36714         }
36715         if(this.toolbar){
36716             var te = this.toolbar.getEl();
36717             te.setWidth(width);
36718             height -= te.getHeight();
36719         }
36720         if(this.footer){
36721             var te = this.footer.getEl();
36722             te.setWidth(width);
36723             height -= te.getHeight();
36724         }
36725         
36726         
36727         if(this.adjustments){
36728             width += this.adjustments[0];
36729             height += this.adjustments[1];
36730         }
36731         return {"width": width, "height": height};
36732     },
36733     
36734     setSize : function(width, height){
36735         if(this.fitToFrame && !this.ignoreResize(width, height)){
36736             if(this.fitContainer && this.resizeEl != this.el){
36737                 this.el.setSize(width, height);
36738             }
36739             var size = this.adjustForComponents(width, height);
36740             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36741             this.fireEvent('resize', this, size.width, size.height);
36742         }
36743     },
36744     
36745     /**
36746      * Returns this panel's title
36747      * @return {String} 
36748      */
36749     getTitle : function(){
36750         
36751         if (typeof(this.title) != 'object') {
36752             return this.title;
36753         }
36754         
36755         var t = '';
36756         for (var k in this.title) {
36757             if (!this.title.hasOwnProperty(k)) {
36758                 continue;
36759             }
36760             
36761             if (k.indexOf('-') >= 0) {
36762                 var s = k.split('-');
36763                 for (var i = 0; i<s.length; i++) {
36764                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36765                 }
36766             } else {
36767                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36768             }
36769         }
36770         return t;
36771     },
36772     
36773     /**
36774      * Set this panel's title
36775      * @param {String} title
36776      */
36777     setTitle : function(title){
36778         this.title = title;
36779         if(this.region){
36780             this.region.updatePanelTitle(this, title);
36781         }
36782     },
36783     
36784     /**
36785      * Returns true is this panel was configured to be closable
36786      * @return {Boolean} 
36787      */
36788     isClosable : function(){
36789         return this.closable;
36790     },
36791     
36792     beforeSlide : function(){
36793         this.el.clip();
36794         this.resizeEl.clip();
36795     },
36796     
36797     afterSlide : function(){
36798         this.el.unclip();
36799         this.resizeEl.unclip();
36800     },
36801     
36802     /**
36803      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36804      *   Will fail silently if the {@link #setUrl} method has not been called.
36805      *   This does not activate the panel, just updates its content.
36806      */
36807     refresh : function(){
36808         if(this.refreshDelegate){
36809            this.loaded = false;
36810            this.refreshDelegate();
36811         }
36812     },
36813     
36814     /**
36815      * Destroys this panel
36816      */
36817     destroy : function(){
36818         this.el.removeAllListeners();
36819         var tempEl = document.createElement("span");
36820         tempEl.appendChild(this.el.dom);
36821         tempEl.innerHTML = "";
36822         this.el.remove();
36823         this.el = null;
36824     },
36825     
36826     /**
36827      * form - if the content panel contains a form - this is a reference to it.
36828      * @type {Roo.form.Form}
36829      */
36830     form : false,
36831     /**
36832      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36833      *    This contains a reference to it.
36834      * @type {Roo.View}
36835      */
36836     view : false,
36837     
36838       /**
36839      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36840      * <pre><code>
36841
36842 layout.addxtype({
36843        xtype : 'Form',
36844        items: [ .... ]
36845    }
36846 );
36847
36848 </code></pre>
36849      * @param {Object} cfg Xtype definition of item to add.
36850      */
36851     
36852     
36853     getChildContainer: function () {
36854         return this.getEl();
36855     }
36856     
36857     
36858     /*
36859         var  ret = new Roo.factory(cfg);
36860         return ret;
36861         
36862         
36863         // add form..
36864         if (cfg.xtype.match(/^Form$/)) {
36865             
36866             var el;
36867             //if (this.footer) {
36868             //    el = this.footer.container.insertSibling(false, 'before');
36869             //} else {
36870                 el = this.el.createChild();
36871             //}
36872
36873             this.form = new  Roo.form.Form(cfg);
36874             
36875             
36876             if ( this.form.allItems.length) {
36877                 this.form.render(el.dom);
36878             }
36879             return this.form;
36880         }
36881         // should only have one of theses..
36882         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36883             // views.. should not be just added - used named prop 'view''
36884             
36885             cfg.el = this.el.appendChild(document.createElement("div"));
36886             // factory?
36887             
36888             var ret = new Roo.factory(cfg);
36889              
36890              ret.render && ret.render(false, ''); // render blank..
36891             this.view = ret;
36892             return ret;
36893         }
36894         return false;
36895     }
36896     \*/
36897 });
36898  
36899 /**
36900  * @class Roo.bootstrap.panel.Grid
36901  * @extends Roo.bootstrap.panel.Content
36902  * @constructor
36903  * Create a new GridPanel.
36904  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36905  * @param {Object} config A the config object
36906   
36907  */
36908
36909
36910
36911 Roo.bootstrap.panel.Grid = function(config)
36912 {
36913     
36914       
36915     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36916         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36917
36918     config.el = this.wrapper;
36919     //this.el = this.wrapper;
36920     
36921       if (config.container) {
36922         // ctor'ed from a Border/panel.grid
36923         
36924         
36925         this.wrapper.setStyle("overflow", "hidden");
36926         this.wrapper.addClass('roo-grid-container');
36927
36928     }
36929     
36930     
36931     if(config.toolbar){
36932         var tool_el = this.wrapper.createChild();    
36933         this.toolbar = Roo.factory(config.toolbar);
36934         var ti = [];
36935         if (config.toolbar.items) {
36936             ti = config.toolbar.items ;
36937             delete config.toolbar.items ;
36938         }
36939         
36940         var nitems = [];
36941         this.toolbar.render(tool_el);
36942         for(var i =0;i < ti.length;i++) {
36943           //  Roo.log(['add child', items[i]]);
36944             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36945         }
36946         this.toolbar.items = nitems;
36947         
36948         delete config.toolbar;
36949     }
36950     
36951     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36952     config.grid.scrollBody = true;;
36953     config.grid.monitorWindowResize = false; // turn off autosizing
36954     config.grid.autoHeight = false;
36955     config.grid.autoWidth = false;
36956     
36957     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36958     
36959     if (config.background) {
36960         // render grid on panel activation (if panel background)
36961         this.on('activate', function(gp) {
36962             if (!gp.grid.rendered) {
36963                 gp.grid.render(this.wrapper);
36964                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36965             }
36966         });
36967             
36968     } else {
36969         this.grid.render(this.wrapper);
36970         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36971
36972     }
36973     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36974     // ??? needed ??? config.el = this.wrapper;
36975     
36976     
36977     
36978   
36979     // xtype created footer. - not sure if will work as we normally have to render first..
36980     if (this.footer && !this.footer.el && this.footer.xtype) {
36981         
36982         var ctr = this.grid.getView().getFooterPanel(true);
36983         this.footer.dataSource = this.grid.dataSource;
36984         this.footer = Roo.factory(this.footer, Roo);
36985         this.footer.render(ctr);
36986         
36987     }
36988     
36989     
36990     
36991     
36992      
36993 };
36994
36995 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36996     getId : function(){
36997         return this.grid.id;
36998     },
36999     
37000     /**
37001      * Returns the grid for this panel
37002      * @return {Roo.bootstrap.Table} 
37003      */
37004     getGrid : function(){
37005         return this.grid;    
37006     },
37007     
37008     setSize : function(width, height){
37009         if(!this.ignoreResize(width, height)){
37010             var grid = this.grid;
37011             var size = this.adjustForComponents(width, height);
37012             var gridel = grid.getGridEl();
37013             gridel.setSize(size.width, size.height);
37014             /*
37015             var thd = grid.getGridEl().select('thead',true).first();
37016             var tbd = grid.getGridEl().select('tbody', true).first();
37017             if (tbd) {
37018                 tbd.setSize(width, height - thd.getHeight());
37019             }
37020             */
37021             grid.autoSize();
37022         }
37023     },
37024      
37025     
37026     
37027     beforeSlide : function(){
37028         this.grid.getView().scroller.clip();
37029     },
37030     
37031     afterSlide : function(){
37032         this.grid.getView().scroller.unclip();
37033     },
37034     
37035     destroy : function(){
37036         this.grid.destroy();
37037         delete this.grid;
37038         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37039     }
37040 });
37041
37042 /**
37043  * @class Roo.bootstrap.panel.Nest
37044  * @extends Roo.bootstrap.panel.Content
37045  * @constructor
37046  * Create a new Panel, that can contain a layout.Border.
37047  * 
37048  * 
37049  * @param {Roo.BorderLayout} layout The layout for this panel
37050  * @param {String/Object} config A string to set only the title or a config object
37051  */
37052 Roo.bootstrap.panel.Nest = function(config)
37053 {
37054     // construct with only one argument..
37055     /* FIXME - implement nicer consturctors
37056     if (layout.layout) {
37057         config = layout;
37058         layout = config.layout;
37059         delete config.layout;
37060     }
37061     if (layout.xtype && !layout.getEl) {
37062         // then layout needs constructing..
37063         layout = Roo.factory(layout, Roo);
37064     }
37065     */
37066     
37067     config.el =  config.layout.getEl();
37068     
37069     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37070     
37071     config.layout.monitorWindowResize = false; // turn off autosizing
37072     this.layout = config.layout;
37073     this.layout.getEl().addClass("roo-layout-nested-layout");
37074     
37075     
37076     
37077     
37078 };
37079
37080 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37081
37082     setSize : function(width, height){
37083         if(!this.ignoreResize(width, height)){
37084             var size = this.adjustForComponents(width, height);
37085             var el = this.layout.getEl();
37086             if (size.height < 1) {
37087                 el.setWidth(size.width);   
37088             } else {
37089                 el.setSize(size.width, size.height);
37090             }
37091             var touch = el.dom.offsetWidth;
37092             this.layout.layout();
37093             // ie requires a double layout on the first pass
37094             if(Roo.isIE && !this.initialized){
37095                 this.initialized = true;
37096                 this.layout.layout();
37097             }
37098         }
37099     },
37100     
37101     // activate all subpanels if not currently active..
37102     
37103     setActiveState : function(active){
37104         this.active = active;
37105         this.setActiveClass(active);
37106         
37107         if(!active){
37108             this.fireEvent("deactivate", this);
37109             return;
37110         }
37111         
37112         this.fireEvent("activate", this);
37113         // not sure if this should happen before or after..
37114         if (!this.layout) {
37115             return; // should not happen..
37116         }
37117         var reg = false;
37118         for (var r in this.layout.regions) {
37119             reg = this.layout.getRegion(r);
37120             if (reg.getActivePanel()) {
37121                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37122                 reg.setActivePanel(reg.getActivePanel());
37123                 continue;
37124             }
37125             if (!reg.panels.length) {
37126                 continue;
37127             }
37128             reg.showPanel(reg.getPanel(0));
37129         }
37130         
37131         
37132         
37133         
37134     },
37135     
37136     /**
37137      * Returns the nested BorderLayout for this panel
37138      * @return {Roo.BorderLayout} 
37139      */
37140     getLayout : function(){
37141         return this.layout;
37142     },
37143     
37144      /**
37145      * Adds a xtype elements to the layout of the nested panel
37146      * <pre><code>
37147
37148 panel.addxtype({
37149        xtype : 'ContentPanel',
37150        region: 'west',
37151        items: [ .... ]
37152    }
37153 );
37154
37155 panel.addxtype({
37156         xtype : 'NestedLayoutPanel',
37157         region: 'west',
37158         layout: {
37159            center: { },
37160            west: { }   
37161         },
37162         items : [ ... list of content panels or nested layout panels.. ]
37163    }
37164 );
37165 </code></pre>
37166      * @param {Object} cfg Xtype definition of item to add.
37167      */
37168     addxtype : function(cfg) {
37169         return this.layout.addxtype(cfg);
37170     
37171     }
37172 });        /*
37173  * Based on:
37174  * Ext JS Library 1.1.1
37175  * Copyright(c) 2006-2007, Ext JS, LLC.
37176  *
37177  * Originally Released Under LGPL - original licence link has changed is not relivant.
37178  *
37179  * Fork - LGPL
37180  * <script type="text/javascript">
37181  */
37182 /**
37183  * @class Roo.TabPanel
37184  * @extends Roo.util.Observable
37185  * A lightweight tab container.
37186  * <br><br>
37187  * Usage:
37188  * <pre><code>
37189 // basic tabs 1, built from existing content
37190 var tabs = new Roo.TabPanel("tabs1");
37191 tabs.addTab("script", "View Script");
37192 tabs.addTab("markup", "View Markup");
37193 tabs.activate("script");
37194
37195 // more advanced tabs, built from javascript
37196 var jtabs = new Roo.TabPanel("jtabs");
37197 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37198
37199 // set up the UpdateManager
37200 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37201 var updater = tab2.getUpdateManager();
37202 updater.setDefaultUrl("ajax1.htm");
37203 tab2.on('activate', updater.refresh, updater, true);
37204
37205 // Use setUrl for Ajax loading
37206 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37207 tab3.setUrl("ajax2.htm", null, true);
37208
37209 // Disabled tab
37210 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37211 tab4.disable();
37212
37213 jtabs.activate("jtabs-1");
37214  * </code></pre>
37215  * @constructor
37216  * Create a new TabPanel.
37217  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37218  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37219  */
37220 Roo.bootstrap.panel.Tabs = function(config){
37221     /**
37222     * The container element for this TabPanel.
37223     * @type Roo.Element
37224     */
37225     this.el = Roo.get(config.el);
37226     delete config.el;
37227     if(config){
37228         if(typeof config == "boolean"){
37229             this.tabPosition = config ? "bottom" : "top";
37230         }else{
37231             Roo.apply(this, config);
37232         }
37233     }
37234     
37235     if(this.tabPosition == "bottom"){
37236         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37237         this.el.addClass("roo-tabs-bottom");
37238     }
37239     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37240     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37241     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37242     if(Roo.isIE){
37243         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37244     }
37245     if(this.tabPosition != "bottom"){
37246         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37247          * @type Roo.Element
37248          */
37249         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37250         this.el.addClass("roo-tabs-top");
37251     }
37252     this.items = [];
37253
37254     this.bodyEl.setStyle("position", "relative");
37255
37256     this.active = null;
37257     this.activateDelegate = this.activate.createDelegate(this);
37258
37259     this.addEvents({
37260         /**
37261          * @event tabchange
37262          * Fires when the active tab changes
37263          * @param {Roo.TabPanel} this
37264          * @param {Roo.TabPanelItem} activePanel The new active tab
37265          */
37266         "tabchange": true,
37267         /**
37268          * @event beforetabchange
37269          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37270          * @param {Roo.TabPanel} this
37271          * @param {Object} e Set cancel to true on this object to cancel the tab change
37272          * @param {Roo.TabPanelItem} tab The tab being changed to
37273          */
37274         "beforetabchange" : true
37275     });
37276
37277     Roo.EventManager.onWindowResize(this.onResize, this);
37278     this.cpad = this.el.getPadding("lr");
37279     this.hiddenCount = 0;
37280
37281
37282     // toolbar on the tabbar support...
37283     if (this.toolbar) {
37284         alert("no toolbar support yet");
37285         this.toolbar  = false;
37286         /*
37287         var tcfg = this.toolbar;
37288         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37289         this.toolbar = new Roo.Toolbar(tcfg);
37290         if (Roo.isSafari) {
37291             var tbl = tcfg.container.child('table', true);
37292             tbl.setAttribute('width', '100%');
37293         }
37294         */
37295         
37296     }
37297    
37298
37299
37300     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37301 };
37302
37303 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37304     /*
37305      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37306      */
37307     tabPosition : "top",
37308     /*
37309      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37310      */
37311     currentTabWidth : 0,
37312     /*
37313      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37314      */
37315     minTabWidth : 40,
37316     /*
37317      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37318      */
37319     maxTabWidth : 250,
37320     /*
37321      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37322      */
37323     preferredTabWidth : 175,
37324     /*
37325      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37326      */
37327     resizeTabs : false,
37328     /*
37329      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37330      */
37331     monitorResize : true,
37332     /*
37333      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37334      */
37335     toolbar : false,
37336
37337     /**
37338      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37339      * @param {String} id The id of the div to use <b>or create</b>
37340      * @param {String} text The text for the tab
37341      * @param {String} content (optional) Content to put in the TabPanelItem body
37342      * @param {Boolean} closable (optional) True to create a close icon on the tab
37343      * @return {Roo.TabPanelItem} The created TabPanelItem
37344      */
37345     addTab : function(id, text, content, closable, tpl)
37346     {
37347         var item = new Roo.bootstrap.panel.TabItem({
37348             panel: this,
37349             id : id,
37350             text : text,
37351             closable : closable,
37352             tpl : tpl
37353         });
37354         this.addTabItem(item);
37355         if(content){
37356             item.setContent(content);
37357         }
37358         return item;
37359     },
37360
37361     /**
37362      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37363      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37364      * @return {Roo.TabPanelItem}
37365      */
37366     getTab : function(id){
37367         return this.items[id];
37368     },
37369
37370     /**
37371      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37372      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37373      */
37374     hideTab : function(id){
37375         var t = this.items[id];
37376         if(!t.isHidden()){
37377            t.setHidden(true);
37378            this.hiddenCount++;
37379            this.autoSizeTabs();
37380         }
37381     },
37382
37383     /**
37384      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37385      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37386      */
37387     unhideTab : function(id){
37388         var t = this.items[id];
37389         if(t.isHidden()){
37390            t.setHidden(false);
37391            this.hiddenCount--;
37392            this.autoSizeTabs();
37393         }
37394     },
37395
37396     /**
37397      * Adds an existing {@link Roo.TabPanelItem}.
37398      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37399      */
37400     addTabItem : function(item){
37401         this.items[item.id] = item;
37402         this.items.push(item);
37403       //  if(this.resizeTabs){
37404     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37405   //         this.autoSizeTabs();
37406 //        }else{
37407 //            item.autoSize();
37408        // }
37409     },
37410
37411     /**
37412      * Removes a {@link Roo.TabPanelItem}.
37413      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37414      */
37415     removeTab : function(id){
37416         var items = this.items;
37417         var tab = items[id];
37418         if(!tab) { return; }
37419         var index = items.indexOf(tab);
37420         if(this.active == tab && items.length > 1){
37421             var newTab = this.getNextAvailable(index);
37422             if(newTab) {
37423                 newTab.activate();
37424             }
37425         }
37426         this.stripEl.dom.removeChild(tab.pnode.dom);
37427         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37428             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37429         }
37430         items.splice(index, 1);
37431         delete this.items[tab.id];
37432         tab.fireEvent("close", tab);
37433         tab.purgeListeners();
37434         this.autoSizeTabs();
37435     },
37436
37437     getNextAvailable : function(start){
37438         var items = this.items;
37439         var index = start;
37440         // look for a next tab that will slide over to
37441         // replace the one being removed
37442         while(index < items.length){
37443             var item = items[++index];
37444             if(item && !item.isHidden()){
37445                 return item;
37446             }
37447         }
37448         // if one isn't found select the previous tab (on the left)
37449         index = start;
37450         while(index >= 0){
37451             var item = items[--index];
37452             if(item && !item.isHidden()){
37453                 return item;
37454             }
37455         }
37456         return null;
37457     },
37458
37459     /**
37460      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37461      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37462      */
37463     disableTab : function(id){
37464         var tab = this.items[id];
37465         if(tab && this.active != tab){
37466             tab.disable();
37467         }
37468     },
37469
37470     /**
37471      * Enables a {@link Roo.TabPanelItem} that is disabled.
37472      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37473      */
37474     enableTab : function(id){
37475         var tab = this.items[id];
37476         tab.enable();
37477     },
37478
37479     /**
37480      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37481      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37482      * @return {Roo.TabPanelItem} The TabPanelItem.
37483      */
37484     activate : function(id){
37485         var tab = this.items[id];
37486         if(!tab){
37487             return null;
37488         }
37489         if(tab == this.active || tab.disabled){
37490             return tab;
37491         }
37492         var e = {};
37493         this.fireEvent("beforetabchange", this, e, tab);
37494         if(e.cancel !== true && !tab.disabled){
37495             if(this.active){
37496                 this.active.hide();
37497             }
37498             this.active = this.items[id];
37499             this.active.show();
37500             this.fireEvent("tabchange", this, this.active);
37501         }
37502         return tab;
37503     },
37504
37505     /**
37506      * Gets the active {@link Roo.TabPanelItem}.
37507      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37508      */
37509     getActiveTab : function(){
37510         return this.active;
37511     },
37512
37513     /**
37514      * Updates the tab body element to fit the height of the container element
37515      * for overflow scrolling
37516      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37517      */
37518     syncHeight : function(targetHeight){
37519         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37520         var bm = this.bodyEl.getMargins();
37521         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37522         this.bodyEl.setHeight(newHeight);
37523         return newHeight;
37524     },
37525
37526     onResize : function(){
37527         if(this.monitorResize){
37528             this.autoSizeTabs();
37529         }
37530     },
37531
37532     /**
37533      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37534      */
37535     beginUpdate : function(){
37536         this.updating = true;
37537     },
37538
37539     /**
37540      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37541      */
37542     endUpdate : function(){
37543         this.updating = false;
37544         this.autoSizeTabs();
37545     },
37546
37547     /**
37548      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37549      */
37550     autoSizeTabs : function(){
37551         var count = this.items.length;
37552         var vcount = count - this.hiddenCount;
37553         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37554             return;
37555         }
37556         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37557         var availWidth = Math.floor(w / vcount);
37558         var b = this.stripBody;
37559         if(b.getWidth() > w){
37560             var tabs = this.items;
37561             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37562             if(availWidth < this.minTabWidth){
37563                 /*if(!this.sleft){    // incomplete scrolling code
37564                     this.createScrollButtons();
37565                 }
37566                 this.showScroll();
37567                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37568             }
37569         }else{
37570             if(this.currentTabWidth < this.preferredTabWidth){
37571                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37572             }
37573         }
37574     },
37575
37576     /**
37577      * Returns the number of tabs in this TabPanel.
37578      * @return {Number}
37579      */
37580      getCount : function(){
37581          return this.items.length;
37582      },
37583
37584     /**
37585      * Resizes all the tabs to the passed width
37586      * @param {Number} The new width
37587      */
37588     setTabWidth : function(width){
37589         this.currentTabWidth = width;
37590         for(var i = 0, len = this.items.length; i < len; i++) {
37591                 if(!this.items[i].isHidden()) {
37592                 this.items[i].setWidth(width);
37593             }
37594         }
37595     },
37596
37597     /**
37598      * Destroys this TabPanel
37599      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37600      */
37601     destroy : function(removeEl){
37602         Roo.EventManager.removeResizeListener(this.onResize, this);
37603         for(var i = 0, len = this.items.length; i < len; i++){
37604             this.items[i].purgeListeners();
37605         }
37606         if(removeEl === true){
37607             this.el.update("");
37608             this.el.remove();
37609         }
37610     },
37611     
37612     createStrip : function(container)
37613     {
37614         var strip = document.createElement("nav");
37615         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37616         container.appendChild(strip);
37617         return strip;
37618     },
37619     
37620     createStripList : function(strip)
37621     {
37622         // div wrapper for retard IE
37623         // returns the "tr" element.
37624         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37625         //'<div class="x-tabs-strip-wrap">'+
37626           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37627           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37628         return strip.firstChild; //.firstChild.firstChild.firstChild;
37629     },
37630     createBody : function(container)
37631     {
37632         var body = document.createElement("div");
37633         Roo.id(body, "tab-body");
37634         //Roo.fly(body).addClass("x-tabs-body");
37635         Roo.fly(body).addClass("tab-content");
37636         container.appendChild(body);
37637         return body;
37638     },
37639     createItemBody :function(bodyEl, id){
37640         var body = Roo.getDom(id);
37641         if(!body){
37642             body = document.createElement("div");
37643             body.id = id;
37644         }
37645         //Roo.fly(body).addClass("x-tabs-item-body");
37646         Roo.fly(body).addClass("tab-pane");
37647          bodyEl.insertBefore(body, bodyEl.firstChild);
37648         return body;
37649     },
37650     /** @private */
37651     createStripElements :  function(stripEl, text, closable, tpl)
37652     {
37653         var td = document.createElement("li"); // was td..
37654         
37655         
37656         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37657         
37658         
37659         stripEl.appendChild(td);
37660         /*if(closable){
37661             td.className = "x-tabs-closable";
37662             if(!this.closeTpl){
37663                 this.closeTpl = new Roo.Template(
37664                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37665                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37666                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37667                 );
37668             }
37669             var el = this.closeTpl.overwrite(td, {"text": text});
37670             var close = el.getElementsByTagName("div")[0];
37671             var inner = el.getElementsByTagName("em")[0];
37672             return {"el": el, "close": close, "inner": inner};
37673         } else {
37674         */
37675         // not sure what this is..
37676 //            if(!this.tabTpl){
37677                 //this.tabTpl = new Roo.Template(
37678                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37679                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37680                 //);
37681 //                this.tabTpl = new Roo.Template(
37682 //                   '<a href="#">' +
37683 //                   '<span unselectable="on"' +
37684 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37685 //                            ' >{text}</span></a>'
37686 //                );
37687 //                
37688 //            }
37689
37690
37691             var template = tpl || this.tabTpl || false;
37692             
37693             if(!template){
37694                 
37695                 template = new Roo.Template(
37696                    '<a href="#">' +
37697                    '<span unselectable="on"' +
37698                             (this.disableTooltips ? '' : ' title="{text}"') +
37699                             ' >{text}</span></a>'
37700                 );
37701             }
37702             
37703             switch (typeof(template)) {
37704                 case 'object' :
37705                     break;
37706                 case 'string' :
37707                     template = new Roo.Template(template);
37708                     break;
37709                 default :
37710                     break;
37711             }
37712             
37713             var el = template.overwrite(td, {"text": text});
37714             
37715             var inner = el.getElementsByTagName("span")[0];
37716             
37717             return {"el": el, "inner": inner};
37718             
37719     }
37720         
37721     
37722 });
37723
37724 /**
37725  * @class Roo.TabPanelItem
37726  * @extends Roo.util.Observable
37727  * Represents an individual item (tab plus body) in a TabPanel.
37728  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37729  * @param {String} id The id of this TabPanelItem
37730  * @param {String} text The text for the tab of this TabPanelItem
37731  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37732  */
37733 Roo.bootstrap.panel.TabItem = function(config){
37734     /**
37735      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37736      * @type Roo.TabPanel
37737      */
37738     this.tabPanel = config.panel;
37739     /**
37740      * The id for this TabPanelItem
37741      * @type String
37742      */
37743     this.id = config.id;
37744     /** @private */
37745     this.disabled = false;
37746     /** @private */
37747     this.text = config.text;
37748     /** @private */
37749     this.loaded = false;
37750     this.closable = config.closable;
37751
37752     /**
37753      * The body element for this TabPanelItem.
37754      * @type Roo.Element
37755      */
37756     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37757     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37758     this.bodyEl.setStyle("display", "block");
37759     this.bodyEl.setStyle("zoom", "1");
37760     //this.hideAction();
37761
37762     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37763     /** @private */
37764     this.el = Roo.get(els.el);
37765     this.inner = Roo.get(els.inner, true);
37766     this.textEl = Roo.get(this.el.dom.firstChild, true);
37767     this.pnode = Roo.get(els.el.parentNode, true);
37768 //    this.el.on("mousedown", this.onTabMouseDown, this);
37769     this.el.on("click", this.onTabClick, this);
37770     /** @private */
37771     if(config.closable){
37772         var c = Roo.get(els.close, true);
37773         c.dom.title = this.closeText;
37774         c.addClassOnOver("close-over");
37775         c.on("click", this.closeClick, this);
37776      }
37777
37778     this.addEvents({
37779          /**
37780          * @event activate
37781          * Fires when this tab becomes the active tab.
37782          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37783          * @param {Roo.TabPanelItem} this
37784          */
37785         "activate": true,
37786         /**
37787          * @event beforeclose
37788          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37789          * @param {Roo.TabPanelItem} this
37790          * @param {Object} e Set cancel to true on this object to cancel the close.
37791          */
37792         "beforeclose": true,
37793         /**
37794          * @event close
37795          * Fires when this tab is closed.
37796          * @param {Roo.TabPanelItem} this
37797          */
37798          "close": true,
37799         /**
37800          * @event deactivate
37801          * Fires when this tab is no longer the active tab.
37802          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37803          * @param {Roo.TabPanelItem} this
37804          */
37805          "deactivate" : true
37806     });
37807     this.hidden = false;
37808
37809     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37810 };
37811
37812 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37813            {
37814     purgeListeners : function(){
37815        Roo.util.Observable.prototype.purgeListeners.call(this);
37816        this.el.removeAllListeners();
37817     },
37818     /**
37819      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37820      */
37821     show : function(){
37822         this.pnode.addClass("active");
37823         this.showAction();
37824         if(Roo.isOpera){
37825             this.tabPanel.stripWrap.repaint();
37826         }
37827         this.fireEvent("activate", this.tabPanel, this);
37828     },
37829
37830     /**
37831      * Returns true if this tab is the active tab.
37832      * @return {Boolean}
37833      */
37834     isActive : function(){
37835         return this.tabPanel.getActiveTab() == this;
37836     },
37837
37838     /**
37839      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37840      */
37841     hide : function(){
37842         this.pnode.removeClass("active");
37843         this.hideAction();
37844         this.fireEvent("deactivate", this.tabPanel, this);
37845     },
37846
37847     hideAction : function(){
37848         this.bodyEl.hide();
37849         this.bodyEl.setStyle("position", "absolute");
37850         this.bodyEl.setLeft("-20000px");
37851         this.bodyEl.setTop("-20000px");
37852     },
37853
37854     showAction : function(){
37855         this.bodyEl.setStyle("position", "relative");
37856         this.bodyEl.setTop("");
37857         this.bodyEl.setLeft("");
37858         this.bodyEl.show();
37859     },
37860
37861     /**
37862      * Set the tooltip for the tab.
37863      * @param {String} tooltip The tab's tooltip
37864      */
37865     setTooltip : function(text){
37866         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37867             this.textEl.dom.qtip = text;
37868             this.textEl.dom.removeAttribute('title');
37869         }else{
37870             this.textEl.dom.title = text;
37871         }
37872     },
37873
37874     onTabClick : function(e){
37875         e.preventDefault();
37876         this.tabPanel.activate(this.id);
37877     },
37878
37879     onTabMouseDown : function(e){
37880         e.preventDefault();
37881         this.tabPanel.activate(this.id);
37882     },
37883 /*
37884     getWidth : function(){
37885         return this.inner.getWidth();
37886     },
37887
37888     setWidth : function(width){
37889         var iwidth = width - this.pnode.getPadding("lr");
37890         this.inner.setWidth(iwidth);
37891         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37892         this.pnode.setWidth(width);
37893     },
37894 */
37895     /**
37896      * Show or hide the tab
37897      * @param {Boolean} hidden True to hide or false to show.
37898      */
37899     setHidden : function(hidden){
37900         this.hidden = hidden;
37901         this.pnode.setStyle("display", hidden ? "none" : "");
37902     },
37903
37904     /**
37905      * Returns true if this tab is "hidden"
37906      * @return {Boolean}
37907      */
37908     isHidden : function(){
37909         return this.hidden;
37910     },
37911
37912     /**
37913      * Returns the text for this tab
37914      * @return {String}
37915      */
37916     getText : function(){
37917         return this.text;
37918     },
37919     /*
37920     autoSize : function(){
37921         //this.el.beginMeasure();
37922         this.textEl.setWidth(1);
37923         /*
37924          *  #2804 [new] Tabs in Roojs
37925          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37926          */
37927         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37928         //this.el.endMeasure();
37929     //},
37930
37931     /**
37932      * Sets the text for the tab (Note: this also sets the tooltip text)
37933      * @param {String} text The tab's text and tooltip
37934      */
37935     setText : function(text){
37936         this.text = text;
37937         this.textEl.update(text);
37938         this.setTooltip(text);
37939         //if(!this.tabPanel.resizeTabs){
37940         //    this.autoSize();
37941         //}
37942     },
37943     /**
37944      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37945      */
37946     activate : function(){
37947         this.tabPanel.activate(this.id);
37948     },
37949
37950     /**
37951      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37952      */
37953     disable : function(){
37954         if(this.tabPanel.active != this){
37955             this.disabled = true;
37956             this.pnode.addClass("disabled");
37957         }
37958     },
37959
37960     /**
37961      * Enables this TabPanelItem if it was previously disabled.
37962      */
37963     enable : function(){
37964         this.disabled = false;
37965         this.pnode.removeClass("disabled");
37966     },
37967
37968     /**
37969      * Sets the content for this TabPanelItem.
37970      * @param {String} content The content
37971      * @param {Boolean} loadScripts true to look for and load scripts
37972      */
37973     setContent : function(content, loadScripts){
37974         this.bodyEl.update(content, loadScripts);
37975     },
37976
37977     /**
37978      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37979      * @return {Roo.UpdateManager} The UpdateManager
37980      */
37981     getUpdateManager : function(){
37982         return this.bodyEl.getUpdateManager();
37983     },
37984
37985     /**
37986      * Set a URL to be used to load the content for this TabPanelItem.
37987      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37988      * @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)
37989      * @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)
37990      * @return {Roo.UpdateManager} The UpdateManager
37991      */
37992     setUrl : function(url, params, loadOnce){
37993         if(this.refreshDelegate){
37994             this.un('activate', this.refreshDelegate);
37995         }
37996         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37997         this.on("activate", this.refreshDelegate);
37998         return this.bodyEl.getUpdateManager();
37999     },
38000
38001     /** @private */
38002     _handleRefresh : function(url, params, loadOnce){
38003         if(!loadOnce || !this.loaded){
38004             var updater = this.bodyEl.getUpdateManager();
38005             updater.update(url, params, this._setLoaded.createDelegate(this));
38006         }
38007     },
38008
38009     /**
38010      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38011      *   Will fail silently if the setUrl method has not been called.
38012      *   This does not activate the panel, just updates its content.
38013      */
38014     refresh : function(){
38015         if(this.refreshDelegate){
38016            this.loaded = false;
38017            this.refreshDelegate();
38018         }
38019     },
38020
38021     /** @private */
38022     _setLoaded : function(){
38023         this.loaded = true;
38024     },
38025
38026     /** @private */
38027     closeClick : function(e){
38028         var o = {};
38029         e.stopEvent();
38030         this.fireEvent("beforeclose", this, o);
38031         if(o.cancel !== true){
38032             this.tabPanel.removeTab(this.id);
38033         }
38034     },
38035     /**
38036      * The text displayed in the tooltip for the close icon.
38037      * @type String
38038      */
38039     closeText : "Close this tab"
38040 });
38041 /**
38042 *    This script refer to:
38043 *    Title: International Telephone Input
38044 *    Author: Jack O'Connor
38045 *    Code version:  v12.1.12
38046 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38047 **/
38048
38049 Roo.bootstrap.PhoneInputData = function() {
38050     var d = [
38051       [
38052         "Afghanistan (‫افغانستان‬‎)",
38053         "af",
38054         "93"
38055       ],
38056       [
38057         "Albania (Shqipëri)",
38058         "al",
38059         "355"
38060       ],
38061       [
38062         "Algeria (‫الجزائر‬‎)",
38063         "dz",
38064         "213"
38065       ],
38066       [
38067         "American Samoa",
38068         "as",
38069         "1684"
38070       ],
38071       [
38072         "Andorra",
38073         "ad",
38074         "376"
38075       ],
38076       [
38077         "Angola",
38078         "ao",
38079         "244"
38080       ],
38081       [
38082         "Anguilla",
38083         "ai",
38084         "1264"
38085       ],
38086       [
38087         "Antigua and Barbuda",
38088         "ag",
38089         "1268"
38090       ],
38091       [
38092         "Argentina",
38093         "ar",
38094         "54"
38095       ],
38096       [
38097         "Armenia (Հայաստան)",
38098         "am",
38099         "374"
38100       ],
38101       [
38102         "Aruba",
38103         "aw",
38104         "297"
38105       ],
38106       [
38107         "Australia",
38108         "au",
38109         "61",
38110         0
38111       ],
38112       [
38113         "Austria (Österreich)",
38114         "at",
38115         "43"
38116       ],
38117       [
38118         "Azerbaijan (Azərbaycan)",
38119         "az",
38120         "994"
38121       ],
38122       [
38123         "Bahamas",
38124         "bs",
38125         "1242"
38126       ],
38127       [
38128         "Bahrain (‫البحرين‬‎)",
38129         "bh",
38130         "973"
38131       ],
38132       [
38133         "Bangladesh (বাংলাদেশ)",
38134         "bd",
38135         "880"
38136       ],
38137       [
38138         "Barbados",
38139         "bb",
38140         "1246"
38141       ],
38142       [
38143         "Belarus (Беларусь)",
38144         "by",
38145         "375"
38146       ],
38147       [
38148         "Belgium (België)",
38149         "be",
38150         "32"
38151       ],
38152       [
38153         "Belize",
38154         "bz",
38155         "501"
38156       ],
38157       [
38158         "Benin (Bénin)",
38159         "bj",
38160         "229"
38161       ],
38162       [
38163         "Bermuda",
38164         "bm",
38165         "1441"
38166       ],
38167       [
38168         "Bhutan (འབྲུག)",
38169         "bt",
38170         "975"
38171       ],
38172       [
38173         "Bolivia",
38174         "bo",
38175         "591"
38176       ],
38177       [
38178         "Bosnia and Herzegovina (Босна и Херцеговина)",
38179         "ba",
38180         "387"
38181       ],
38182       [
38183         "Botswana",
38184         "bw",
38185         "267"
38186       ],
38187       [
38188         "Brazil (Brasil)",
38189         "br",
38190         "55"
38191       ],
38192       [
38193         "British Indian Ocean Territory",
38194         "io",
38195         "246"
38196       ],
38197       [
38198         "British Virgin Islands",
38199         "vg",
38200         "1284"
38201       ],
38202       [
38203         "Brunei",
38204         "bn",
38205         "673"
38206       ],
38207       [
38208         "Bulgaria (България)",
38209         "bg",
38210         "359"
38211       ],
38212       [
38213         "Burkina Faso",
38214         "bf",
38215         "226"
38216       ],
38217       [
38218         "Burundi (Uburundi)",
38219         "bi",
38220         "257"
38221       ],
38222       [
38223         "Cambodia (កម្ពុជា)",
38224         "kh",
38225         "855"
38226       ],
38227       [
38228         "Cameroon (Cameroun)",
38229         "cm",
38230         "237"
38231       ],
38232       [
38233         "Canada",
38234         "ca",
38235         "1",
38236         1,
38237         ["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"]
38238       ],
38239       [
38240         "Cape Verde (Kabu Verdi)",
38241         "cv",
38242         "238"
38243       ],
38244       [
38245         "Caribbean Netherlands",
38246         "bq",
38247         "599",
38248         1
38249       ],
38250       [
38251         "Cayman Islands",
38252         "ky",
38253         "1345"
38254       ],
38255       [
38256         "Central African Republic (République centrafricaine)",
38257         "cf",
38258         "236"
38259       ],
38260       [
38261         "Chad (Tchad)",
38262         "td",
38263         "235"
38264       ],
38265       [
38266         "Chile",
38267         "cl",
38268         "56"
38269       ],
38270       [
38271         "China (中国)",
38272         "cn",
38273         "86"
38274       ],
38275       [
38276         "Christmas Island",
38277         "cx",
38278         "61",
38279         2
38280       ],
38281       [
38282         "Cocos (Keeling) Islands",
38283         "cc",
38284         "61",
38285         1
38286       ],
38287       [
38288         "Colombia",
38289         "co",
38290         "57"
38291       ],
38292       [
38293         "Comoros (‫جزر القمر‬‎)",
38294         "km",
38295         "269"
38296       ],
38297       [
38298         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38299         "cd",
38300         "243"
38301       ],
38302       [
38303         "Congo (Republic) (Congo-Brazzaville)",
38304         "cg",
38305         "242"
38306       ],
38307       [
38308         "Cook Islands",
38309         "ck",
38310         "682"
38311       ],
38312       [
38313         "Costa Rica",
38314         "cr",
38315         "506"
38316       ],
38317       [
38318         "Côte d’Ivoire",
38319         "ci",
38320         "225"
38321       ],
38322       [
38323         "Croatia (Hrvatska)",
38324         "hr",
38325         "385"
38326       ],
38327       [
38328         "Cuba",
38329         "cu",
38330         "53"
38331       ],
38332       [
38333         "Curaçao",
38334         "cw",
38335         "599",
38336         0
38337       ],
38338       [
38339         "Cyprus (Κύπρος)",
38340         "cy",
38341         "357"
38342       ],
38343       [
38344         "Czech Republic (Česká republika)",
38345         "cz",
38346         "420"
38347       ],
38348       [
38349         "Denmark (Danmark)",
38350         "dk",
38351         "45"
38352       ],
38353       [
38354         "Djibouti",
38355         "dj",
38356         "253"
38357       ],
38358       [
38359         "Dominica",
38360         "dm",
38361         "1767"
38362       ],
38363       [
38364         "Dominican Republic (República Dominicana)",
38365         "do",
38366         "1",
38367         2,
38368         ["809", "829", "849"]
38369       ],
38370       [
38371         "Ecuador",
38372         "ec",
38373         "593"
38374       ],
38375       [
38376         "Egypt (‫مصر‬‎)",
38377         "eg",
38378         "20"
38379       ],
38380       [
38381         "El Salvador",
38382         "sv",
38383         "503"
38384       ],
38385       [
38386         "Equatorial Guinea (Guinea Ecuatorial)",
38387         "gq",
38388         "240"
38389       ],
38390       [
38391         "Eritrea",
38392         "er",
38393         "291"
38394       ],
38395       [
38396         "Estonia (Eesti)",
38397         "ee",
38398         "372"
38399       ],
38400       [
38401         "Ethiopia",
38402         "et",
38403         "251"
38404       ],
38405       [
38406         "Falkland Islands (Islas Malvinas)",
38407         "fk",
38408         "500"
38409       ],
38410       [
38411         "Faroe Islands (Føroyar)",
38412         "fo",
38413         "298"
38414       ],
38415       [
38416         "Fiji",
38417         "fj",
38418         "679"
38419       ],
38420       [
38421         "Finland (Suomi)",
38422         "fi",
38423         "358",
38424         0
38425       ],
38426       [
38427         "France",
38428         "fr",
38429         "33"
38430       ],
38431       [
38432         "French Guiana (Guyane française)",
38433         "gf",
38434         "594"
38435       ],
38436       [
38437         "French Polynesia (Polynésie française)",
38438         "pf",
38439         "689"
38440       ],
38441       [
38442         "Gabon",
38443         "ga",
38444         "241"
38445       ],
38446       [
38447         "Gambia",
38448         "gm",
38449         "220"
38450       ],
38451       [
38452         "Georgia (საქართველო)",
38453         "ge",
38454         "995"
38455       ],
38456       [
38457         "Germany (Deutschland)",
38458         "de",
38459         "49"
38460       ],
38461       [
38462         "Ghana (Gaana)",
38463         "gh",
38464         "233"
38465       ],
38466       [
38467         "Gibraltar",
38468         "gi",
38469         "350"
38470       ],
38471       [
38472         "Greece (Ελλάδα)",
38473         "gr",
38474         "30"
38475       ],
38476       [
38477         "Greenland (Kalaallit Nunaat)",
38478         "gl",
38479         "299"
38480       ],
38481       [
38482         "Grenada",
38483         "gd",
38484         "1473"
38485       ],
38486       [
38487         "Guadeloupe",
38488         "gp",
38489         "590",
38490         0
38491       ],
38492       [
38493         "Guam",
38494         "gu",
38495         "1671"
38496       ],
38497       [
38498         "Guatemala",
38499         "gt",
38500         "502"
38501       ],
38502       [
38503         "Guernsey",
38504         "gg",
38505         "44",
38506         1
38507       ],
38508       [
38509         "Guinea (Guinée)",
38510         "gn",
38511         "224"
38512       ],
38513       [
38514         "Guinea-Bissau (Guiné Bissau)",
38515         "gw",
38516         "245"
38517       ],
38518       [
38519         "Guyana",
38520         "gy",
38521         "592"
38522       ],
38523       [
38524         "Haiti",
38525         "ht",
38526         "509"
38527       ],
38528       [
38529         "Honduras",
38530         "hn",
38531         "504"
38532       ],
38533       [
38534         "Hong Kong (香港)",
38535         "hk",
38536         "852"
38537       ],
38538       [
38539         "Hungary (Magyarország)",
38540         "hu",
38541         "36"
38542       ],
38543       [
38544         "Iceland (Ísland)",
38545         "is",
38546         "354"
38547       ],
38548       [
38549         "India (भारत)",
38550         "in",
38551         "91"
38552       ],
38553       [
38554         "Indonesia",
38555         "id",
38556         "62"
38557       ],
38558       [
38559         "Iran (‫ایران‬‎)",
38560         "ir",
38561         "98"
38562       ],
38563       [
38564         "Iraq (‫العراق‬‎)",
38565         "iq",
38566         "964"
38567       ],
38568       [
38569         "Ireland",
38570         "ie",
38571         "353"
38572       ],
38573       [
38574         "Isle of Man",
38575         "im",
38576         "44",
38577         2
38578       ],
38579       [
38580         "Israel (‫ישראל‬‎)",
38581         "il",
38582         "972"
38583       ],
38584       [
38585         "Italy (Italia)",
38586         "it",
38587         "39",
38588         0
38589       ],
38590       [
38591         "Jamaica",
38592         "jm",
38593         "1876"
38594       ],
38595       [
38596         "Japan (日本)",
38597         "jp",
38598         "81"
38599       ],
38600       [
38601         "Jersey",
38602         "je",
38603         "44",
38604         3
38605       ],
38606       [
38607         "Jordan (‫الأردن‬‎)",
38608         "jo",
38609         "962"
38610       ],
38611       [
38612         "Kazakhstan (Казахстан)",
38613         "kz",
38614         "7",
38615         1
38616       ],
38617       [
38618         "Kenya",
38619         "ke",
38620         "254"
38621       ],
38622       [
38623         "Kiribati",
38624         "ki",
38625         "686"
38626       ],
38627       [
38628         "Kosovo",
38629         "xk",
38630         "383"
38631       ],
38632       [
38633         "Kuwait (‫الكويت‬‎)",
38634         "kw",
38635         "965"
38636       ],
38637       [
38638         "Kyrgyzstan (Кыргызстан)",
38639         "kg",
38640         "996"
38641       ],
38642       [
38643         "Laos (ລາວ)",
38644         "la",
38645         "856"
38646       ],
38647       [
38648         "Latvia (Latvija)",
38649         "lv",
38650         "371"
38651       ],
38652       [
38653         "Lebanon (‫لبنان‬‎)",
38654         "lb",
38655         "961"
38656       ],
38657       [
38658         "Lesotho",
38659         "ls",
38660         "266"
38661       ],
38662       [
38663         "Liberia",
38664         "lr",
38665         "231"
38666       ],
38667       [
38668         "Libya (‫ليبيا‬‎)",
38669         "ly",
38670         "218"
38671       ],
38672       [
38673         "Liechtenstein",
38674         "li",
38675         "423"
38676       ],
38677       [
38678         "Lithuania (Lietuva)",
38679         "lt",
38680         "370"
38681       ],
38682       [
38683         "Luxembourg",
38684         "lu",
38685         "352"
38686       ],
38687       [
38688         "Macau (澳門)",
38689         "mo",
38690         "853"
38691       ],
38692       [
38693         "Macedonia (FYROM) (Македонија)",
38694         "mk",
38695         "389"
38696       ],
38697       [
38698         "Madagascar (Madagasikara)",
38699         "mg",
38700         "261"
38701       ],
38702       [
38703         "Malawi",
38704         "mw",
38705         "265"
38706       ],
38707       [
38708         "Malaysia",
38709         "my",
38710         "60"
38711       ],
38712       [
38713         "Maldives",
38714         "mv",
38715         "960"
38716       ],
38717       [
38718         "Mali",
38719         "ml",
38720         "223"
38721       ],
38722       [
38723         "Malta",
38724         "mt",
38725         "356"
38726       ],
38727       [
38728         "Marshall Islands",
38729         "mh",
38730         "692"
38731       ],
38732       [
38733         "Martinique",
38734         "mq",
38735         "596"
38736       ],
38737       [
38738         "Mauritania (‫موريتانيا‬‎)",
38739         "mr",
38740         "222"
38741       ],
38742       [
38743         "Mauritius (Moris)",
38744         "mu",
38745         "230"
38746       ],
38747       [
38748         "Mayotte",
38749         "yt",
38750         "262",
38751         1
38752       ],
38753       [
38754         "Mexico (México)",
38755         "mx",
38756         "52"
38757       ],
38758       [
38759         "Micronesia",
38760         "fm",
38761         "691"
38762       ],
38763       [
38764         "Moldova (Republica Moldova)",
38765         "md",
38766         "373"
38767       ],
38768       [
38769         "Monaco",
38770         "mc",
38771         "377"
38772       ],
38773       [
38774         "Mongolia (Монгол)",
38775         "mn",
38776         "976"
38777       ],
38778       [
38779         "Montenegro (Crna Gora)",
38780         "me",
38781         "382"
38782       ],
38783       [
38784         "Montserrat",
38785         "ms",
38786         "1664"
38787       ],
38788       [
38789         "Morocco (‫المغرب‬‎)",
38790         "ma",
38791         "212",
38792         0
38793       ],
38794       [
38795         "Mozambique (Moçambique)",
38796         "mz",
38797         "258"
38798       ],
38799       [
38800         "Myanmar (Burma) (မြန်မာ)",
38801         "mm",
38802         "95"
38803       ],
38804       [
38805         "Namibia (Namibië)",
38806         "na",
38807         "264"
38808       ],
38809       [
38810         "Nauru",
38811         "nr",
38812         "674"
38813       ],
38814       [
38815         "Nepal (नेपाल)",
38816         "np",
38817         "977"
38818       ],
38819       [
38820         "Netherlands (Nederland)",
38821         "nl",
38822         "31"
38823       ],
38824       [
38825         "New Caledonia (Nouvelle-Calédonie)",
38826         "nc",
38827         "687"
38828       ],
38829       [
38830         "New Zealand",
38831         "nz",
38832         "64"
38833       ],
38834       [
38835         "Nicaragua",
38836         "ni",
38837         "505"
38838       ],
38839       [
38840         "Niger (Nijar)",
38841         "ne",
38842         "227"
38843       ],
38844       [
38845         "Nigeria",
38846         "ng",
38847         "234"
38848       ],
38849       [
38850         "Niue",
38851         "nu",
38852         "683"
38853       ],
38854       [
38855         "Norfolk Island",
38856         "nf",
38857         "672"
38858       ],
38859       [
38860         "North Korea (조선 민주주의 인민 공화국)",
38861         "kp",
38862         "850"
38863       ],
38864       [
38865         "Northern Mariana Islands",
38866         "mp",
38867         "1670"
38868       ],
38869       [
38870         "Norway (Norge)",
38871         "no",
38872         "47",
38873         0
38874       ],
38875       [
38876         "Oman (‫عُمان‬‎)",
38877         "om",
38878         "968"
38879       ],
38880       [
38881         "Pakistan (‫پاکستان‬‎)",
38882         "pk",
38883         "92"
38884       ],
38885       [
38886         "Palau",
38887         "pw",
38888         "680"
38889       ],
38890       [
38891         "Palestine (‫فلسطين‬‎)",
38892         "ps",
38893         "970"
38894       ],
38895       [
38896         "Panama (Panamá)",
38897         "pa",
38898         "507"
38899       ],
38900       [
38901         "Papua New Guinea",
38902         "pg",
38903         "675"
38904       ],
38905       [
38906         "Paraguay",
38907         "py",
38908         "595"
38909       ],
38910       [
38911         "Peru (Perú)",
38912         "pe",
38913         "51"
38914       ],
38915       [
38916         "Philippines",
38917         "ph",
38918         "63"
38919       ],
38920       [
38921         "Poland (Polska)",
38922         "pl",
38923         "48"
38924       ],
38925       [
38926         "Portugal",
38927         "pt",
38928         "351"
38929       ],
38930       [
38931         "Puerto Rico",
38932         "pr",
38933         "1",
38934         3,
38935         ["787", "939"]
38936       ],
38937       [
38938         "Qatar (‫قطر‬‎)",
38939         "qa",
38940         "974"
38941       ],
38942       [
38943         "Réunion (La Réunion)",
38944         "re",
38945         "262",
38946         0
38947       ],
38948       [
38949         "Romania (România)",
38950         "ro",
38951         "40"
38952       ],
38953       [
38954         "Russia (Россия)",
38955         "ru",
38956         "7",
38957         0
38958       ],
38959       [
38960         "Rwanda",
38961         "rw",
38962         "250"
38963       ],
38964       [
38965         "Saint Barthélemy",
38966         "bl",
38967         "590",
38968         1
38969       ],
38970       [
38971         "Saint Helena",
38972         "sh",
38973         "290"
38974       ],
38975       [
38976         "Saint Kitts and Nevis",
38977         "kn",
38978         "1869"
38979       ],
38980       [
38981         "Saint Lucia",
38982         "lc",
38983         "1758"
38984       ],
38985       [
38986         "Saint Martin (Saint-Martin (partie française))",
38987         "mf",
38988         "590",
38989         2
38990       ],
38991       [
38992         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38993         "pm",
38994         "508"
38995       ],
38996       [
38997         "Saint Vincent and the Grenadines",
38998         "vc",
38999         "1784"
39000       ],
39001       [
39002         "Samoa",
39003         "ws",
39004         "685"
39005       ],
39006       [
39007         "San Marino",
39008         "sm",
39009         "378"
39010       ],
39011       [
39012         "São Tomé and Príncipe (São Tomé e Príncipe)",
39013         "st",
39014         "239"
39015       ],
39016       [
39017         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39018         "sa",
39019         "966"
39020       ],
39021       [
39022         "Senegal (Sénégal)",
39023         "sn",
39024         "221"
39025       ],
39026       [
39027         "Serbia (Србија)",
39028         "rs",
39029         "381"
39030       ],
39031       [
39032         "Seychelles",
39033         "sc",
39034         "248"
39035       ],
39036       [
39037         "Sierra Leone",
39038         "sl",
39039         "232"
39040       ],
39041       [
39042         "Singapore",
39043         "sg",
39044         "65"
39045       ],
39046       [
39047         "Sint Maarten",
39048         "sx",
39049         "1721"
39050       ],
39051       [
39052         "Slovakia (Slovensko)",
39053         "sk",
39054         "421"
39055       ],
39056       [
39057         "Slovenia (Slovenija)",
39058         "si",
39059         "386"
39060       ],
39061       [
39062         "Solomon Islands",
39063         "sb",
39064         "677"
39065       ],
39066       [
39067         "Somalia (Soomaaliya)",
39068         "so",
39069         "252"
39070       ],
39071       [
39072         "South Africa",
39073         "za",
39074         "27"
39075       ],
39076       [
39077         "South Korea (대한민국)",
39078         "kr",
39079         "82"
39080       ],
39081       [
39082         "South Sudan (‫جنوب السودان‬‎)",
39083         "ss",
39084         "211"
39085       ],
39086       [
39087         "Spain (España)",
39088         "es",
39089         "34"
39090       ],
39091       [
39092         "Sri Lanka (ශ්‍රී ලංකාව)",
39093         "lk",
39094         "94"
39095       ],
39096       [
39097         "Sudan (‫السودان‬‎)",
39098         "sd",
39099         "249"
39100       ],
39101       [
39102         "Suriname",
39103         "sr",
39104         "597"
39105       ],
39106       [
39107         "Svalbard and Jan Mayen",
39108         "sj",
39109         "47",
39110         1
39111       ],
39112       [
39113         "Swaziland",
39114         "sz",
39115         "268"
39116       ],
39117       [
39118         "Sweden (Sverige)",
39119         "se",
39120         "46"
39121       ],
39122       [
39123         "Switzerland (Schweiz)",
39124         "ch",
39125         "41"
39126       ],
39127       [
39128         "Syria (‫سوريا‬‎)",
39129         "sy",
39130         "963"
39131       ],
39132       [
39133         "Taiwan (台灣)",
39134         "tw",
39135         "886"
39136       ],
39137       [
39138         "Tajikistan",
39139         "tj",
39140         "992"
39141       ],
39142       [
39143         "Tanzania",
39144         "tz",
39145         "255"
39146       ],
39147       [
39148         "Thailand (ไทย)",
39149         "th",
39150         "66"
39151       ],
39152       [
39153         "Timor-Leste",
39154         "tl",
39155         "670"
39156       ],
39157       [
39158         "Togo",
39159         "tg",
39160         "228"
39161       ],
39162       [
39163         "Tokelau",
39164         "tk",
39165         "690"
39166       ],
39167       [
39168         "Tonga",
39169         "to",
39170         "676"
39171       ],
39172       [
39173         "Trinidad and Tobago",
39174         "tt",
39175         "1868"
39176       ],
39177       [
39178         "Tunisia (‫تونس‬‎)",
39179         "tn",
39180         "216"
39181       ],
39182       [
39183         "Turkey (Türkiye)",
39184         "tr",
39185         "90"
39186       ],
39187       [
39188         "Turkmenistan",
39189         "tm",
39190         "993"
39191       ],
39192       [
39193         "Turks and Caicos Islands",
39194         "tc",
39195         "1649"
39196       ],
39197       [
39198         "Tuvalu",
39199         "tv",
39200         "688"
39201       ],
39202       [
39203         "U.S. Virgin Islands",
39204         "vi",
39205         "1340"
39206       ],
39207       [
39208         "Uganda",
39209         "ug",
39210         "256"
39211       ],
39212       [
39213         "Ukraine (Україна)",
39214         "ua",
39215         "380"
39216       ],
39217       [
39218         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39219         "ae",
39220         "971"
39221       ],
39222       [
39223         "United Kingdom",
39224         "gb",
39225         "44",
39226         0
39227       ],
39228       [
39229         "United States",
39230         "us",
39231         "1",
39232         0
39233       ],
39234       [
39235         "Uruguay",
39236         "uy",
39237         "598"
39238       ],
39239       [
39240         "Uzbekistan (Oʻzbekiston)",
39241         "uz",
39242         "998"
39243       ],
39244       [
39245         "Vanuatu",
39246         "vu",
39247         "678"
39248       ],
39249       [
39250         "Vatican City (Città del Vaticano)",
39251         "va",
39252         "39",
39253         1
39254       ],
39255       [
39256         "Venezuela",
39257         "ve",
39258         "58"
39259       ],
39260       [
39261         "Vietnam (Việt Nam)",
39262         "vn",
39263         "84"
39264       ],
39265       [
39266         "Wallis and Futuna (Wallis-et-Futuna)",
39267         "wf",
39268         "681"
39269       ],
39270       [
39271         "Western Sahara (‫الصحراء الغربية‬‎)",
39272         "eh",
39273         "212",
39274         1
39275       ],
39276       [
39277         "Yemen (‫اليمن‬‎)",
39278         "ye",
39279         "967"
39280       ],
39281       [
39282         "Zambia",
39283         "zm",
39284         "260"
39285       ],
39286       [
39287         "Zimbabwe",
39288         "zw",
39289         "263"
39290       ],
39291       [
39292         "Åland Islands",
39293         "ax",
39294         "358",
39295         1
39296       ]
39297   ];
39298   
39299   return d;
39300 }/**
39301 *    This script refer to:
39302 *    Title: International Telephone Input
39303 *    Author: Jack O'Connor
39304 *    Code version:  v12.1.12
39305 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39306 **/
39307
39308 /**
39309  * @class Roo.bootstrap.PhoneInput
39310  * @extends Roo.bootstrap.TriggerField
39311  * An input with International dial-code selection
39312  
39313  * @cfg {String} defaultDialCode default '+852'
39314  * @cfg {Array} preferedCountries default []
39315   
39316  * @constructor
39317  * Create a new PhoneInput.
39318  * @param {Object} config Configuration options
39319  */
39320
39321 Roo.bootstrap.PhoneInput = function(config) {
39322     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39323 };
39324
39325 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39326         
39327         listWidth: undefined,
39328         
39329         selectedClass: 'active',
39330         
39331         invalidClass : "has-warning",
39332         
39333         validClass: 'has-success',
39334         
39335         allowed: '0123456789',
39336         
39337         /**
39338          * @cfg {String} defaultDialCode The default dial code when initializing the input
39339          */
39340         defaultDialCode: '+852',
39341         
39342         /**
39343          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39344          */
39345         preferedCountries: false,
39346         
39347         getAutoCreate : function()
39348         {
39349             var data = Roo.bootstrap.PhoneInputData();
39350             var align = this.labelAlign || this.parentLabelAlign();
39351             var id = Roo.id();
39352             
39353             this.allCountries = [];
39354             this.dialCodeMapping = [];
39355             
39356             for (var i = 0; i < data.length; i++) {
39357               var c = data[i];
39358               this.allCountries[i] = {
39359                 name: c[0],
39360                 iso2: c[1],
39361                 dialCode: c[2],
39362                 priority: c[3] || 0,
39363                 areaCodes: c[4] || null
39364               };
39365               this.dialCodeMapping[c[2]] = {
39366                   name: c[0],
39367                   iso2: c[1],
39368                   priority: c[3] || 0,
39369                   areaCodes: c[4] || null
39370               };
39371             }
39372             
39373             var cfg = {
39374                 cls: 'form-group',
39375                 cn: []
39376             };
39377             
39378             var input =  {
39379                 tag: 'input',
39380                 id : id,
39381                 cls : 'form-control tel-input',
39382                 autocomplete: 'new-password'
39383             };
39384             
39385             var hiddenInput = {
39386                 tag: 'input',
39387                 type: 'hidden',
39388                 cls: 'hidden-tel-input'
39389             };
39390             
39391             if (this.name) {
39392                 hiddenInput.name = this.name;
39393             }
39394             
39395             if (this.disabled) {
39396                 input.disabled = true;
39397             }
39398             
39399             var flag_container = {
39400                 tag: 'div',
39401                 cls: 'flag-box',
39402                 cn: [
39403                     {
39404                         tag: 'div',
39405                         cls: 'flag'
39406                     },
39407                     {
39408                         tag: 'div',
39409                         cls: 'caret'
39410                     }
39411                 ]
39412             };
39413             
39414             var box = {
39415                 tag: 'div',
39416                 cls: this.hasFeedback ? 'has-feedback' : '',
39417                 cn: [
39418                     hiddenInput,
39419                     input,
39420                     {
39421                         tag: 'input',
39422                         cls: 'dial-code-holder',
39423                         disabled: true
39424                     }
39425                 ]
39426             };
39427             
39428             var container = {
39429                 cls: 'roo-select2-container input-group',
39430                 cn: [
39431                     flag_container,
39432                     box
39433                 ]
39434             };
39435             
39436             if (this.fieldLabel.length) {
39437                 var indicator = {
39438                     tag: 'i',
39439                     tooltip: 'This field is required'
39440                 };
39441                 
39442                 var label = {
39443                     tag: 'label',
39444                     'for':  id,
39445                     cls: 'control-label',
39446                     cn: []
39447                 };
39448                 
39449                 var label_text = {
39450                     tag: 'span',
39451                     html: this.fieldLabel
39452                 };
39453                 
39454                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39455                 label.cn = [
39456                     indicator,
39457                     label_text
39458                 ];
39459                 
39460                 if(this.indicatorpos == 'right') {
39461                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39462                     label.cn = [
39463                         label_text,
39464                         indicator
39465                     ];
39466                 }
39467                 
39468                 if(align == 'left') {
39469                     container = {
39470                         tag: 'div',
39471                         cn: [
39472                             container
39473                         ]
39474                     };
39475                     
39476                     if(this.labelWidth > 12){
39477                         label.style = "width: " + this.labelWidth + 'px';
39478                     }
39479                     if(this.labelWidth < 13 && this.labelmd == 0){
39480                         this.labelmd = this.labelWidth;
39481                     }
39482                     if(this.labellg > 0){
39483                         label.cls += ' col-lg-' + this.labellg;
39484                         input.cls += ' col-lg-' + (12 - this.labellg);
39485                     }
39486                     if(this.labelmd > 0){
39487                         label.cls += ' col-md-' + this.labelmd;
39488                         container.cls += ' col-md-' + (12 - this.labelmd);
39489                     }
39490                     if(this.labelsm > 0){
39491                         label.cls += ' col-sm-' + this.labelsm;
39492                         container.cls += ' col-sm-' + (12 - this.labelsm);
39493                     }
39494                     if(this.labelxs > 0){
39495                         label.cls += ' col-xs-' + this.labelxs;
39496                         container.cls += ' col-xs-' + (12 - this.labelxs);
39497                     }
39498                 }
39499             }
39500             
39501             cfg.cn = [
39502                 label,
39503                 container
39504             ];
39505             
39506             var settings = this;
39507             
39508             ['xs','sm','md','lg'].map(function(size){
39509                 if (settings[size]) {
39510                     cfg.cls += ' col-' + size + '-' + settings[size];
39511                 }
39512             });
39513             
39514             this.store = new Roo.data.Store({
39515                 proxy : new Roo.data.MemoryProxy({}),
39516                 reader : new Roo.data.JsonReader({
39517                     fields : [
39518                         {
39519                             'name' : 'name',
39520                             'type' : 'string'
39521                         },
39522                         {
39523                             'name' : 'iso2',
39524                             'type' : 'string'
39525                         },
39526                         {
39527                             'name' : 'dialCode',
39528                             'type' : 'string'
39529                         },
39530                         {
39531                             'name' : 'priority',
39532                             'type' : 'string'
39533                         },
39534                         {
39535                             'name' : 'areaCodes',
39536                             'type' : 'string'
39537                         }
39538                     ]
39539                 })
39540             });
39541             
39542             if(!this.preferedCountries) {
39543                 this.preferedCountries = [
39544                     'hk',
39545                     'gb',
39546                     'us'
39547                 ];
39548             }
39549             
39550             var p = this.preferedCountries.reverse();
39551             
39552             if(p) {
39553                 for (var i = 0; i < p.length; i++) {
39554                     for (var j = 0; j < this.allCountries.length; j++) {
39555                         if(this.allCountries[j].iso2 == p[i]) {
39556                             var t = this.allCountries[j];
39557                             this.allCountries.splice(j,1);
39558                             this.allCountries.unshift(t);
39559                         }
39560                     } 
39561                 }
39562             }
39563             
39564             this.store.proxy.data = {
39565                 success: true,
39566                 data: this.allCountries
39567             };
39568             
39569             return cfg;
39570         },
39571         
39572         initEvents : function()
39573         {
39574             this.createList();
39575             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39576             
39577             this.indicator = this.indicatorEl();
39578             this.flag = this.flagEl();
39579             this.dialCodeHolder = this.dialCodeHolderEl();
39580             
39581             this.trigger = this.el.select('div.flag-box',true).first();
39582             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39583             
39584             var _this = this;
39585             
39586             (function(){
39587                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39588                 _this.list.setWidth(lw);
39589             }).defer(100);
39590             
39591             this.list.on('mouseover', this.onViewOver, this);
39592             this.list.on('mousemove', this.onViewMove, this);
39593             this.inputEl().on("keyup", this.onKeyUp, this);
39594             
39595             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39596
39597             this.view = new Roo.View(this.list, this.tpl, {
39598                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39599             });
39600             
39601             this.view.on('click', this.onViewClick, this);
39602             this.setValue(this.defaultDialCode);
39603         },
39604         
39605         onTriggerClick : function(e)
39606         {
39607             Roo.log('trigger click');
39608             if(this.disabled){
39609                 return;
39610             }
39611             
39612             if(this.isExpanded()){
39613                 this.collapse();
39614                 this.hasFocus = false;
39615             }else {
39616                 this.store.load({});
39617                 this.hasFocus = true;
39618                 this.expand();
39619             }
39620         },
39621         
39622         isExpanded : function()
39623         {
39624             return this.list.isVisible();
39625         },
39626         
39627         collapse : function()
39628         {
39629             if(!this.isExpanded()){
39630                 return;
39631             }
39632             this.list.hide();
39633             Roo.get(document).un('mousedown', this.collapseIf, this);
39634             Roo.get(document).un('mousewheel', this.collapseIf, this);
39635             this.fireEvent('collapse', this);
39636             this.validate();
39637         },
39638         
39639         expand : function()
39640         {
39641             Roo.log('expand');
39642
39643             if(this.isExpanded() || !this.hasFocus){
39644                 return;
39645             }
39646             
39647             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39648             this.list.setWidth(lw);
39649             
39650             this.list.show();
39651             this.restrictHeight();
39652             
39653             Roo.get(document).on('mousedown', this.collapseIf, this);
39654             Roo.get(document).on('mousewheel', this.collapseIf, this);
39655             
39656             this.fireEvent('expand', this);
39657         },
39658         
39659         restrictHeight : function()
39660         {
39661             this.list.alignTo(this.inputEl(), this.listAlign);
39662             this.list.alignTo(this.inputEl(), this.listAlign);
39663         },
39664         
39665         onViewOver : function(e, t)
39666         {
39667             if(this.inKeyMode){
39668                 return;
39669             }
39670             var item = this.view.findItemFromChild(t);
39671             
39672             if(item){
39673                 var index = this.view.indexOf(item);
39674                 this.select(index, false);
39675             }
39676         },
39677
39678         // private
39679         onViewClick : function(view, doFocus, el, e)
39680         {
39681             var index = this.view.getSelectedIndexes()[0];
39682             
39683             var r = this.store.getAt(index);
39684             
39685             if(r){
39686                 this.onSelect(r, index);
39687             }
39688             if(doFocus !== false && !this.blockFocus){
39689                 this.inputEl().focus();
39690             }
39691         },
39692         
39693         onViewMove : function(e, t)
39694         {
39695             this.inKeyMode = false;
39696         },
39697         
39698         select : function(index, scrollIntoView)
39699         {
39700             this.selectedIndex = index;
39701             this.view.select(index);
39702             if(scrollIntoView !== false){
39703                 var el = this.view.getNode(index);
39704                 if(el){
39705                     this.list.scrollChildIntoView(el, false);
39706                 }
39707             }
39708         },
39709         
39710         createList : function()
39711         {
39712             this.list = Roo.get(document.body).createChild({
39713                 tag: 'ul',
39714                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39715                 style: 'display:none'
39716             });
39717             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39718         },
39719         
39720         collapseIf : function(e)
39721         {
39722             var in_combo  = e.within(this.el);
39723             var in_list =  e.within(this.list);
39724             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39725             
39726             if (in_combo || in_list || is_list) {
39727                 return;
39728             }
39729             this.collapse();
39730         },
39731         
39732         onSelect : function(record, index)
39733         {
39734             if(this.fireEvent('beforeselect', this, record, index) !== false){
39735                 
39736                 this.setFlagClass(record.data.iso2);
39737                 this.setDialCode(record.data.dialCode);
39738                 this.hasFocus = false;
39739                 this.collapse();
39740                 this.fireEvent('select', this, record, index);
39741             }
39742         },
39743         
39744         flagEl : function()
39745         {
39746             var flag = this.el.select('div.flag',true).first();
39747             if(!flag){
39748                 return false;
39749             }
39750             return flag;
39751         },
39752         
39753         dialCodeHolderEl : function()
39754         {
39755             var d = this.el.select('input.dial-code-holder',true).first();
39756             if(!d){
39757                 return false;
39758             }
39759             return d;
39760         },
39761         
39762         setDialCode : function(v)
39763         {
39764             this.dialCodeHolder.dom.value = '+'+v;
39765         },
39766         
39767         setFlagClass : function(n)
39768         {
39769             this.flag.dom.className = 'flag '+n;
39770         },
39771         
39772         getValue : function()
39773         {
39774             var v = this.inputEl().getValue();
39775             if(this.dialCodeHolder) {
39776                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39777             }
39778             return v;
39779         },
39780         
39781         setValue : function(v)
39782         {
39783             var d = this.getDialCode(v);
39784             
39785             //invalid dial code
39786             if(v.length == 0 || !d || d.length == 0) {
39787                 if(this.rendered){
39788                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39789                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39790                 }
39791                 return;
39792             }
39793             
39794             //valid dial code
39795             this.setFlagClass(this.dialCodeMapping[d].iso2);
39796             this.setDialCode(d);
39797             this.inputEl().dom.value = v.replace('+'+d,'');
39798             this.hiddenEl().dom.value = this.getValue();
39799             
39800             this.validate();
39801         },
39802         
39803         getDialCode : function(v = '')
39804         {
39805             if (v.length == 0) {
39806                 return this.dialCodeHolder.dom.value;
39807             }
39808             
39809             var dialCode = "";
39810             if (v.charAt(0) != "+") {
39811                 return false;
39812             }
39813             var numericChars = "";
39814             for (var i = 1; i < v.length; i++) {
39815               var c = v.charAt(i);
39816               if (!isNaN(c)) {
39817                 numericChars += c;
39818                 if (this.dialCodeMapping[numericChars]) {
39819                   dialCode = v.substr(1, i);
39820                 }
39821                 if (numericChars.length == 4) {
39822                   break;
39823                 }
39824               }
39825             }
39826             return dialCode;
39827         },
39828         
39829         reset : function()
39830         {
39831             this.setValue(this.defaultDialCode);
39832             this.validate();
39833         },
39834         
39835         hiddenEl : function()
39836         {
39837             return this.el.select('input.hidden-tel-input',true).first();
39838         },
39839         
39840         onKeyUp : function(e){
39841             
39842             var k = e.getKey();
39843             var c = e.getCharCode();
39844             
39845             if(
39846                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39847                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39848             ){
39849                 e.stopEvent();
39850             }
39851             
39852             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39853             //     return;
39854             // }
39855             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39856                 e.stopEvent();
39857             }
39858             
39859             this.setValue(this.getValue());
39860         }
39861         
39862 });
39863 /**
39864  * @class Roo.bootstrap.MoneyField
39865  * @extends Roo.bootstrap.ComboBox
39866  * Bootstrap MoneyField class
39867  * 
39868  * @constructor
39869  * Create a new MoneyField.
39870  * @param {Object} config Configuration options
39871  */
39872
39873 Roo.bootstrap.MoneyField = function(config) {
39874     
39875     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39876     
39877 };
39878
39879 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39880     
39881     /**
39882      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39883      */
39884     allowDecimals : true,
39885     /**
39886      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39887      */
39888     decimalSeparator : ".",
39889     /**
39890      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39891      */
39892     decimalPrecision : 2,
39893     /**
39894      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39895      */
39896     allowNegative : true,
39897     /**
39898      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39899      */
39900     minValue : Number.NEGATIVE_INFINITY,
39901     /**
39902      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39903      */
39904     maxValue : Number.MAX_VALUE,
39905     /**
39906      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39907      */
39908     minText : "The minimum value for this field is {0}",
39909     /**
39910      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39911      */
39912     maxText : "The maximum value for this field is {0}",
39913     /**
39914      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39915      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39916      */
39917     nanText : "{0} is not a valid number",
39918     /**
39919      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39920      */
39921     castInt : true,
39922     
39923     inputlg : 9,
39924     inputmd : 9,
39925     inputsm : 9,
39926     inputxs : 6,
39927     
39928     store : false,
39929     
39930     getAutoCreate : function()
39931     {
39932         var align = this.labelAlign || this.parentLabelAlign();
39933         
39934         var id = Roo.id();
39935
39936         var cfg = {
39937             cls: 'form-group',
39938             cn: []
39939         };
39940
39941         var input =  {
39942             tag: 'input',
39943             id : id,
39944             cls : 'form-control roo-money-amount-input',
39945             autocomplete: 'new-password'
39946         };
39947         
39948         if (this.name) {
39949             input.name = this.name;
39950         }
39951
39952         if (this.disabled) {
39953             input.disabled = true;
39954         }
39955
39956         var clg = 12 - this.inputlg;
39957         var cmd = 12 - this.inputmd;
39958         var csm = 12 - this.inputsm;
39959         var cxs = 12 - this.inputxs;
39960         
39961         var container = {
39962             tag : 'div',
39963             cls : 'row roo-money-field',
39964             cn : [
39965                 {
39966                     tag : 'div',
39967                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39968                     cn : [
39969                         {
39970                             tag : 'div',
39971                             cls: 'roo-select2-container input-group',
39972                             cn: [
39973                                 {
39974                                     tag : 'input',
39975                                     cls : 'form-control roo-money-currency-input',
39976                                     autocomplete: 'new-password',
39977                                     readOnly : 1,
39978                                     name : this.currencyName
39979                                 },
39980                                 {
39981                                     tag :'span',
39982                                     cls : 'input-group-addon',
39983                                     cn : [
39984                                         {
39985                                             tag: 'span',
39986                                             cls: 'caret'
39987                                         }
39988                                     ]
39989                                 }
39990                             ]
39991                         }
39992                     ]
39993                 },
39994                 {
39995                     tag : 'div',
39996                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39997                     cn : [
39998                         {
39999                             tag: 'div',
40000                             cls: this.hasFeedback ? 'has-feedback' : '',
40001                             cn: [
40002                                 input
40003                             ]
40004                         }
40005                     ]
40006                 }
40007             ]
40008             
40009         };
40010         
40011         if (this.fieldLabel.length) {
40012             var indicator = {
40013                 tag: 'i',
40014                 tooltip: 'This field is required'
40015             };
40016
40017             var label = {
40018                 tag: 'label',
40019                 'for':  id,
40020                 cls: 'control-label',
40021                 cn: []
40022             };
40023
40024             var label_text = {
40025                 tag: 'span',
40026                 html: this.fieldLabel
40027             };
40028
40029             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40030             label.cn = [
40031                 indicator,
40032                 label_text
40033             ];
40034
40035             if(this.indicatorpos == 'right') {
40036                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40037                 label.cn = [
40038                     label_text,
40039                     indicator
40040                 ];
40041             }
40042
40043             if(align == 'left') {
40044                 container = {
40045                     tag: 'div',
40046                     cn: [
40047                         container
40048                     ]
40049                 };
40050
40051                 if(this.labelWidth > 12){
40052                     label.style = "width: " + this.labelWidth + 'px';
40053                 }
40054                 if(this.labelWidth < 13 && this.labelmd == 0){
40055                     this.labelmd = this.labelWidth;
40056                 }
40057                 if(this.labellg > 0){
40058                     label.cls += ' col-lg-' + this.labellg;
40059                     input.cls += ' col-lg-' + (12 - this.labellg);
40060                 }
40061                 if(this.labelmd > 0){
40062                     label.cls += ' col-md-' + this.labelmd;
40063                     container.cls += ' col-md-' + (12 - this.labelmd);
40064                 }
40065                 if(this.labelsm > 0){
40066                     label.cls += ' col-sm-' + this.labelsm;
40067                     container.cls += ' col-sm-' + (12 - this.labelsm);
40068                 }
40069                 if(this.labelxs > 0){
40070                     label.cls += ' col-xs-' + this.labelxs;
40071                     container.cls += ' col-xs-' + (12 - this.labelxs);
40072                 }
40073             }
40074         }
40075
40076         cfg.cn = [
40077             label,
40078             container
40079         ];
40080
40081         var settings = this;
40082
40083         ['xs','sm','md','lg'].map(function(size){
40084             if (settings[size]) {
40085                 cfg.cls += ' col-' + size + '-' + settings[size];
40086             }
40087         });
40088         
40089         return cfg;
40090         
40091     },
40092     
40093     initEvents : function()
40094     {
40095         this.indicator = this.indicatorEl();
40096         
40097         this.initCurrencyEvent();
40098         
40099         this.initNumberEvent();
40100         
40101     },
40102     
40103     initCurrencyEvent : function()
40104     {
40105         if (!this.store) {
40106             throw "can not find store for combo";
40107         }
40108         
40109         this.store = Roo.factory(this.store, Roo.data);
40110         this.store.parent = this;
40111         
40112         this.createList();
40113         
40114         this.triggerEl = this.el.select('.input-group-addon', true).first();
40115         
40116         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40117         
40118         var _this = this;
40119         
40120         (function(){
40121             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40122             _this.list.setWidth(lw);
40123         }).defer(100);
40124         
40125         this.list.on('mouseover', this.onViewOver, this);
40126         this.list.on('mousemove', this.onViewMove, this);
40127         this.list.on('scroll', this.onViewScroll, this);
40128         
40129         if(!this.tpl){
40130             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40131         }
40132         
40133         this.view = new Roo.View(this.list, this.tpl, {
40134             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40135         });
40136         
40137         this.view.on('click', this.onViewClick, this);
40138         
40139         this.store.on('beforeload', this.onBeforeLoad, this);
40140         this.store.on('load', this.onLoad, this);
40141         this.store.on('loadexception', this.onLoadException, this);
40142         
40143         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40144             "up" : function(e){
40145                 this.inKeyMode = true;
40146                 this.selectPrev();
40147             },
40148
40149             "down" : function(e){
40150                 if(!this.isExpanded()){
40151                     this.onTriggerClick();
40152                 }else{
40153                     this.inKeyMode = true;
40154                     this.selectNext();
40155                 }
40156             },
40157
40158             "enter" : function(e){
40159                 this.collapse();
40160                 
40161                 if(this.fireEvent("specialkey", this, e)){
40162                     this.onViewClick(false);
40163                 }
40164                 
40165                 return true;
40166             },
40167
40168             "esc" : function(e){
40169                 this.collapse();
40170             },
40171
40172             "tab" : function(e){
40173                 this.collapse();
40174                 
40175                 if(this.fireEvent("specialkey", this, e)){
40176                     this.onViewClick(false);
40177                 }
40178                 
40179                 return true;
40180             },
40181
40182             scope : this,
40183
40184             doRelay : function(foo, bar, hname){
40185                 if(hname == 'down' || this.scope.isExpanded()){
40186                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40187                 }
40188                 return true;
40189             },
40190
40191             forceKeyDown: true
40192         });
40193         
40194         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40195         
40196     },
40197     
40198     initNumberEvent : function(e)
40199     {
40200         this.inputEl().on("keydown" , this.fireKey,  this);
40201         this.inputEl().on("focus", this.onFocus,  this);
40202         this.inputEl().on("blur", this.onBlur,  this);
40203         
40204         this.inputEl().relayEvent('keyup', this);
40205         
40206         if(this.indicator){
40207             this.indicator.addClass('invisible');
40208         }
40209  
40210         this.originalValue = this.getValue();
40211         
40212         if(this.validationEvent == 'keyup'){
40213             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40214             this.inputEl().on('keyup', this.filterValidation, this);
40215         }
40216         else if(this.validationEvent !== false){
40217             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40218         }
40219         
40220         if(this.selectOnFocus){
40221             this.on("focus", this.preFocus, this);
40222             
40223         }
40224         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40225             this.inputEl().on("keypress", this.filterKeys, this);
40226         } else {
40227             this.inputEl().relayEvent('keypress', this);
40228         }
40229         
40230         var allowed = "0123456789";
40231         
40232         if(this.allowDecimals){
40233             allowed += this.decimalSeparator;
40234         }
40235         
40236         if(this.allowNegative){
40237             allowed += "-";
40238         }
40239         
40240         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40241         
40242         var keyPress = function(e){
40243             
40244             var k = e.getKey();
40245             
40246             var c = e.getCharCode();
40247             
40248             if(
40249                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40250                     allowed.indexOf(String.fromCharCode(c)) === -1
40251             ){
40252                 e.stopEvent();
40253                 return;
40254             }
40255             
40256             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40257                 return;
40258             }
40259             
40260             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40261                 e.stopEvent();
40262             }
40263         };
40264         
40265         this.inputEl().on("keypress", keyPress, this);
40266         
40267     },
40268     
40269     onTriggerClick : function(e)
40270     {   
40271         if(this.disabled){
40272             return;
40273         }
40274         
40275         this.page = 0;
40276         this.loadNext = false;
40277         
40278         if(this.isExpanded()){
40279             this.collapse();
40280             return;
40281         }
40282         
40283         this.hasFocus = true;
40284         
40285         if(this.triggerAction == 'all') {
40286             this.doQuery(this.allQuery, true);
40287             return;
40288         }
40289         
40290         this.doQuery(this.getRawValue());
40291     },
40292     
40293     getCurrency : function()
40294     {   
40295         var v = this.currencyEl().getValue();
40296         
40297         return v;
40298     },
40299     
40300     restrictHeight : function()
40301     {
40302         this.list.alignTo(this.currencyEl(), this.listAlign);
40303         this.list.alignTo(this.currencyEl(), this.listAlign);
40304     },
40305     
40306     onViewClick : function(view, doFocus, el, e)
40307     {
40308         var index = this.view.getSelectedIndexes()[0];
40309         
40310         var r = this.store.getAt(index);
40311         
40312         if(r){
40313             this.onSelect(r, index);
40314         }
40315     },
40316     
40317     onSelect : function(record, index){
40318         
40319         if(this.fireEvent('beforeselect', this, record, index) !== false){
40320         
40321             this.setFromCurrencyData(index > -1 ? record.data : false);
40322             
40323             this.collapse();
40324             
40325             this.fireEvent('select', this, record, index);
40326         }
40327     },
40328     
40329     setFromCurrencyData : function(o)
40330     {
40331         var currency = '';
40332         
40333         this.lastCurrency = o;
40334         
40335         if (this.currencyField) {
40336             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40337         } else {
40338             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40339         }
40340         
40341         this.lastSelectionText = currency;
40342         
40343         this.setCurrency(currency);
40344     },
40345     
40346     setFromData : function(o)
40347     {
40348         var c = {};
40349         
40350         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40351         
40352         this.setFromCurrencyData(c);
40353         
40354         var value = '';
40355         
40356         if (this.name) {
40357             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40358         } else {
40359             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40360         }
40361         
40362         this.setValue(value);
40363         
40364     },
40365     
40366     setCurrency : function(v)
40367     {   
40368         this.currencyValue = v;
40369         
40370         if(this.rendered){
40371             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40372             this.validate();
40373         }
40374     },
40375     
40376     setValue : function(v)
40377     {
40378         v = this.fixPrecision(v);
40379         
40380         v = String(v).replace(".", this.decimalSeparator);
40381         
40382         this.value = v;
40383         
40384         if(this.rendered){
40385             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40386             this.validate();
40387         }
40388     },
40389     
40390     getRawValue : function()
40391     {
40392         var v = this.inputEl().getValue();
40393         
40394         return v;
40395     },
40396     
40397     getValue : function()
40398     {
40399         return this.fixPrecision(this.parseValue(this.getRawValue()));
40400     },
40401     
40402     parseValue : function(value)
40403     {
40404         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40405         return isNaN(value) ? '' : value;
40406     },
40407     
40408     fixPrecision : function(value)
40409     {
40410         var nan = isNaN(value);
40411         
40412         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40413             return nan ? '' : value;
40414         }
40415         
40416         return parseFloat(value).toFixed(this.decimalPrecision);
40417     },
40418     
40419     decimalPrecisionFcn : function(v)
40420     {
40421         return Math.floor(v);
40422     },
40423     
40424     validateValue : function(value)
40425     {
40426         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40427             return false;
40428         }
40429         
40430         var num = this.parseValue(value);
40431         
40432         if(isNaN(num)){
40433             this.markInvalid(String.format(this.nanText, value));
40434             return false;
40435         }
40436         
40437         if(num < this.minValue){
40438             this.markInvalid(String.format(this.minText, this.minValue));
40439             return false;
40440         }
40441         
40442         if(num > this.maxValue){
40443             this.markInvalid(String.format(this.maxText, this.maxValue));
40444             return false;
40445         }
40446         
40447         return true;
40448     },
40449     
40450     validate : function()
40451     {
40452         if(this.disabled || this.allowBlank){
40453             this.markValid();
40454             return true;
40455         }
40456         
40457         var currency = this.getCurrency();
40458         
40459         if(this.validateValue(this.getRawValue()) && currency.length){
40460             this.markValid();
40461             return true;
40462         }
40463         
40464         this.markInvalid();
40465         return false;
40466     },
40467     
40468     getName: function()
40469     {
40470         return this.name;
40471     },
40472     
40473     beforeBlur : function()
40474     {
40475         if(!this.castInt){
40476             return;
40477         }
40478         
40479         var v = this.parseValue(this.getRawValue());
40480         
40481         if(v){
40482             this.setValue(v);
40483         }
40484     },
40485     
40486     onBlur : function()
40487     {
40488         this.beforeBlur();
40489         
40490         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40491             //this.el.removeClass(this.focusClass);
40492         }
40493         
40494         this.hasFocus = false;
40495         
40496         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40497             this.validate();
40498         }
40499         
40500         var v = this.getValue();
40501         
40502         if(String(v) !== String(this.startValue)){
40503             this.fireEvent('change', this, v, this.startValue);
40504         }
40505         
40506         this.fireEvent("blur", this);
40507     },
40508     
40509     inputEl : function()
40510     {
40511         return this.el.select('.roo-money-amount-input', true).first();
40512     },
40513     
40514     currencyEl : function()
40515     {
40516         return this.el.select('.roo-money-currency-input', true).first();
40517     }
40518     
40519 });