Roo/data/JsonReader.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         
12272         Roo.log('------------------');
12273         Roo.log(o);
12274         
12275         var s = this.meta, Record = this.recordType,
12276             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12277
12278 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12279         if (!this.ef) {
12280             if(s.totalProperty) {
12281                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12282                 }
12283                 if(s.successProperty) {
12284                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12285                 }
12286                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12287                 if (s.id) {
12288                         var g = this.getJsonAccessor(s.id);
12289                         this.getId = function(rec) {
12290                                 var r = g(rec);  
12291                                 return (r === undefined || r === "") ? null : r;
12292                         };
12293                 } else {
12294                         this.getId = function(){return null;};
12295                 }
12296             this.ef = [];
12297             for(var jj = 0; jj < fl; jj++){
12298                 f = fi[jj];
12299                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12300                 this.ef[jj] = this.getJsonAccessor(map);
12301             }
12302         }
12303
12304         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12305         if(s.totalProperty){
12306             var vt = parseInt(this.getTotal(o), 10);
12307             if(!isNaN(vt)){
12308                 totalRecords = vt;
12309             }
12310         }
12311         if(s.successProperty){
12312             var vs = this.getSuccess(o);
12313             if(vs === false || vs === 'false'){
12314                 success = false;
12315             }
12316         }
12317         var records = [];
12318         for(var i = 0; i < c; i++){
12319                 var n = root[i];
12320             var values = {};
12321             var id = this.getId(n);
12322             for(var j = 0; j < fl; j++){
12323                 f = fi[j];
12324             var v = this.ef[j](n);
12325             if (!f.convert) {
12326                 Roo.log('missing convert for ' + f.name);
12327                 Roo.log(f);
12328                 continue;
12329             }
12330             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12331             }
12332             var record = new Record(values, id);
12333             record.json = n;
12334             records[i] = record;
12335         }
12336         return {
12337             raw : o,
12338             success : success,
12339             records : records,
12340             totalRecords : totalRecords
12341         };
12342     }
12343 });/*
12344  * Based on:
12345  * Ext JS Library 1.1.1
12346  * Copyright(c) 2006-2007, Ext JS, LLC.
12347  *
12348  * Originally Released Under LGPL - original licence link has changed is not relivant.
12349  *
12350  * Fork - LGPL
12351  * <script type="text/javascript">
12352  */
12353
12354 /**
12355  * @class Roo.data.ArrayReader
12356  * @extends Roo.data.DataReader
12357  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12358  * Each element of that Array represents a row of data fields. The
12359  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12360  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12361  * <p>
12362  * Example code:.
12363  * <pre><code>
12364 var RecordDef = Roo.data.Record.create([
12365     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12366     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12367 ]);
12368 var myReader = new Roo.data.ArrayReader({
12369     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12370 }, RecordDef);
12371 </code></pre>
12372  * <p>
12373  * This would consume an Array like this:
12374  * <pre><code>
12375 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12376   </code></pre>
12377  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12378  * @constructor
12379  * Create a new JsonReader
12380  * @param {Object} meta Metadata configuration options.
12381  * @param {Object} recordType Either an Array of field definition objects
12382  * as specified to {@link Roo.data.Record#create},
12383  * or an {@link Roo.data.Record} object
12384  * created using {@link Roo.data.Record#create}.
12385  */
12386 Roo.data.ArrayReader = function(meta, recordType){
12387     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12388 };
12389
12390 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12391     /**
12392      * Create a data block containing Roo.data.Records from an XML document.
12393      * @param {Object} o An Array of row objects which represents the dataset.
12394      * @return {Object} data A data block which is used by an Roo.data.Store object as
12395      * a cache of Roo.data.Records.
12396      */
12397     readRecords : function(o){
12398         var sid = this.meta ? this.meta.id : null;
12399         var recordType = this.recordType, fields = recordType.prototype.fields;
12400         var records = [];
12401         var root = o;
12402             for(var i = 0; i < root.length; i++){
12403                     var n = root[i];
12404                 var values = {};
12405                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12406                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12407                 var f = fields.items[j];
12408                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12409                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12410                 v = f.convert(v);
12411                 values[f.name] = v;
12412             }
12413                 var record = new recordType(values, id);
12414                 record.json = n;
12415                 records[records.length] = record;
12416             }
12417             return {
12418                 records : records,
12419                 totalRecords : records.length
12420             };
12421     }
12422 });/*
12423  * - LGPL
12424  * * 
12425  */
12426
12427 /**
12428  * @class Roo.bootstrap.ComboBox
12429  * @extends Roo.bootstrap.TriggerField
12430  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12431  * @cfg {Boolean} append (true|false) default false
12432  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12433  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12434  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12435  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12436  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12437  * @cfg {Boolean} animate default true
12438  * @cfg {Boolean} emptyResultText only for touch device
12439  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12440  * @cfg {String} emptyTitle default ''
12441  * @constructor
12442  * Create a new ComboBox.
12443  * @param {Object} config Configuration options
12444  */
12445 Roo.bootstrap.ComboBox = function(config){
12446     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12447     this.addEvents({
12448         /**
12449          * @event expand
12450          * Fires when the dropdown list is expanded
12451         * @param {Roo.bootstrap.ComboBox} combo This combo box
12452         */
12453         'expand' : true,
12454         /**
12455          * @event collapse
12456          * Fires when the dropdown list is collapsed
12457         * @param {Roo.bootstrap.ComboBox} combo This combo box
12458         */
12459         'collapse' : true,
12460         /**
12461          * @event beforeselect
12462          * Fires before a list item is selected. Return false to cancel the selection.
12463         * @param {Roo.bootstrap.ComboBox} combo This combo box
12464         * @param {Roo.data.Record} record The data record returned from the underlying store
12465         * @param {Number} index The index of the selected item in the dropdown list
12466         */
12467         'beforeselect' : true,
12468         /**
12469          * @event select
12470          * Fires when a list item is selected
12471         * @param {Roo.bootstrap.ComboBox} combo This combo box
12472         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12473         * @param {Number} index The index of the selected item in the dropdown list
12474         */
12475         'select' : true,
12476         /**
12477          * @event beforequery
12478          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12479          * The event object passed has these properties:
12480         * @param {Roo.bootstrap.ComboBox} combo This combo box
12481         * @param {String} query The query
12482         * @param {Boolean} forceAll true to force "all" query
12483         * @param {Boolean} cancel true to cancel the query
12484         * @param {Object} e The query event object
12485         */
12486         'beforequery': true,
12487          /**
12488          * @event add
12489          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12490         * @param {Roo.bootstrap.ComboBox} combo This combo box
12491         */
12492         'add' : true,
12493         /**
12494          * @event edit
12495          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12496         * @param {Roo.bootstrap.ComboBox} combo This combo box
12497         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12498         */
12499         'edit' : true,
12500         /**
12501          * @event remove
12502          * Fires when the remove value from the combobox array
12503         * @param {Roo.bootstrap.ComboBox} combo This combo box
12504         */
12505         'remove' : true,
12506         /**
12507          * @event afterremove
12508          * Fires when the remove value from the combobox array
12509         * @param {Roo.bootstrap.ComboBox} combo This combo box
12510         */
12511         'afterremove' : true,
12512         /**
12513          * @event specialfilter
12514          * Fires when specialfilter
12515             * @param {Roo.bootstrap.ComboBox} combo This combo box
12516             */
12517         'specialfilter' : true,
12518         /**
12519          * @event tick
12520          * Fires when tick the element
12521             * @param {Roo.bootstrap.ComboBox} combo This combo box
12522             */
12523         'tick' : true,
12524         /**
12525          * @event touchviewdisplay
12526          * Fires when touch view require special display (default is using displayField)
12527             * @param {Roo.bootstrap.ComboBox} combo This combo box
12528             * @param {Object} cfg set html .
12529             */
12530         'touchviewdisplay' : true
12531         
12532     });
12533     
12534     this.item = [];
12535     this.tickItems = [];
12536     
12537     this.selectedIndex = -1;
12538     if(this.mode == 'local'){
12539         if(config.queryDelay === undefined){
12540             this.queryDelay = 10;
12541         }
12542         if(config.minChars === undefined){
12543             this.minChars = 0;
12544         }
12545     }
12546 };
12547
12548 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12549      
12550     /**
12551      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12552      * rendering into an Roo.Editor, defaults to false)
12553      */
12554     /**
12555      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12556      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12557      */
12558     /**
12559      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12560      */
12561     /**
12562      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12563      * the dropdown list (defaults to undefined, with no header element)
12564      */
12565
12566      /**
12567      * @cfg {String/Roo.Template} tpl The template to use to render the output
12568      */
12569      
12570      /**
12571      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12572      */
12573     listWidth: undefined,
12574     /**
12575      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12576      * mode = 'remote' or 'text' if mode = 'local')
12577      */
12578     displayField: undefined,
12579     
12580     /**
12581      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12582      * mode = 'remote' or 'value' if mode = 'local'). 
12583      * Note: use of a valueField requires the user make a selection
12584      * in order for a value to be mapped.
12585      */
12586     valueField: undefined,
12587     /**
12588      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12589      */
12590     modalTitle : '',
12591     
12592     /**
12593      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12594      * field's data value (defaults to the underlying DOM element's name)
12595      */
12596     hiddenName: undefined,
12597     /**
12598      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12599      */
12600     listClass: '',
12601     /**
12602      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12603      */
12604     selectedClass: 'active',
12605     
12606     /**
12607      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12608      */
12609     shadow:'sides',
12610     /**
12611      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12612      * anchor positions (defaults to 'tl-bl')
12613      */
12614     listAlign: 'tl-bl?',
12615     /**
12616      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12617      */
12618     maxHeight: 300,
12619     /**
12620      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12621      * query specified by the allQuery config option (defaults to 'query')
12622      */
12623     triggerAction: 'query',
12624     /**
12625      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12626      * (defaults to 4, does not apply if editable = false)
12627      */
12628     minChars : 4,
12629     /**
12630      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12631      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12632      */
12633     typeAhead: false,
12634     /**
12635      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12636      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12637      */
12638     queryDelay: 500,
12639     /**
12640      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12641      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12642      */
12643     pageSize: 0,
12644     /**
12645      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12646      * when editable = true (defaults to false)
12647      */
12648     selectOnFocus:false,
12649     /**
12650      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12651      */
12652     queryParam: 'query',
12653     /**
12654      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12655      * when mode = 'remote' (defaults to 'Loading...')
12656      */
12657     loadingText: 'Loading...',
12658     /**
12659      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12660      */
12661     resizable: false,
12662     /**
12663      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12664      */
12665     handleHeight : 8,
12666     /**
12667      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12668      * traditional select (defaults to true)
12669      */
12670     editable: true,
12671     /**
12672      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12673      */
12674     allQuery: '',
12675     /**
12676      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12677      */
12678     mode: 'remote',
12679     /**
12680      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12681      * listWidth has a higher value)
12682      */
12683     minListWidth : 70,
12684     /**
12685      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12686      * allow the user to set arbitrary text into the field (defaults to false)
12687      */
12688     forceSelection:false,
12689     /**
12690      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12691      * if typeAhead = true (defaults to 250)
12692      */
12693     typeAheadDelay : 250,
12694     /**
12695      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12696      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12697      */
12698     valueNotFoundText : undefined,
12699     /**
12700      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12701      */
12702     blockFocus : false,
12703     
12704     /**
12705      * @cfg {Boolean} disableClear Disable showing of clear button.
12706      */
12707     disableClear : false,
12708     /**
12709      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12710      */
12711     alwaysQuery : false,
12712     
12713     /**
12714      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12715      */
12716     multiple : false,
12717     
12718     /**
12719      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12720      */
12721     invalidClass : "has-warning",
12722     
12723     /**
12724      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12725      */
12726     validClass : "has-success",
12727     
12728     /**
12729      * @cfg {Boolean} specialFilter (true|false) special filter default false
12730      */
12731     specialFilter : false,
12732     
12733     /**
12734      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12735      */
12736     mobileTouchView : true,
12737     
12738     /**
12739      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12740      */
12741     useNativeIOS : false,
12742     
12743     ios_options : false,
12744     
12745     //private
12746     addicon : false,
12747     editicon: false,
12748     
12749     page: 0,
12750     hasQuery: false,
12751     append: false,
12752     loadNext: false,
12753     autoFocus : true,
12754     tickable : false,
12755     btnPosition : 'right',
12756     triggerList : true,
12757     showToggleBtn : true,
12758     animate : true,
12759     emptyResultText: 'Empty',
12760     triggerText : 'Select',
12761     emptyTitle : '',
12762     
12763     // element that contains real text value.. (when hidden is used..)
12764     
12765     getAutoCreate : function()
12766     {   
12767         var cfg = false;
12768         //render
12769         /*
12770          * Render classic select for iso
12771          */
12772         
12773         if(Roo.isIOS && this.useNativeIOS){
12774             cfg = this.getAutoCreateNativeIOS();
12775             return cfg;
12776         }
12777         
12778         /*
12779          * Touch Devices
12780          */
12781         
12782         if(Roo.isTouch && this.mobileTouchView){
12783             cfg = this.getAutoCreateTouchView();
12784             return cfg;;
12785         }
12786         
12787         /*
12788          *  Normal ComboBox
12789          */
12790         if(!this.tickable){
12791             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12792             return cfg;
12793         }
12794         
12795         /*
12796          *  ComboBox with tickable selections
12797          */
12798              
12799         var align = this.labelAlign || this.parentLabelAlign();
12800         
12801         cfg = {
12802             cls : 'form-group roo-combobox-tickable' //input-group
12803         };
12804         
12805         var btn_text_select = '';
12806         var btn_text_done = '';
12807         var btn_text_cancel = '';
12808         
12809         if (this.btn_text_show) {
12810             btn_text_select = 'Select';
12811             btn_text_done = 'Done';
12812             btn_text_cancel = 'Cancel'; 
12813         }
12814         
12815         var buttons = {
12816             tag : 'div',
12817             cls : 'tickable-buttons',
12818             cn : [
12819                 {
12820                     tag : 'button',
12821                     type : 'button',
12822                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12823                     //html : this.triggerText
12824                     html: btn_text_select
12825                 },
12826                 {
12827                     tag : 'button',
12828                     type : 'button',
12829                     name : 'ok',
12830                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12831                     //html : 'Done'
12832                     html: btn_text_done
12833                 },
12834                 {
12835                     tag : 'button',
12836                     type : 'button',
12837                     name : 'cancel',
12838                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12839                     //html : 'Cancel'
12840                     html: btn_text_cancel
12841                 }
12842             ]
12843         };
12844         
12845         if(this.editable){
12846             buttons.cn.unshift({
12847                 tag: 'input',
12848                 cls: 'roo-select2-search-field-input'
12849             });
12850         }
12851         
12852         var _this = this;
12853         
12854         Roo.each(buttons.cn, function(c){
12855             if (_this.size) {
12856                 c.cls += ' btn-' + _this.size;
12857             }
12858
12859             if (_this.disabled) {
12860                 c.disabled = true;
12861             }
12862         });
12863         
12864         var box = {
12865             tag: 'div',
12866             cn: [
12867                 {
12868                     tag: 'input',
12869                     type : 'hidden',
12870                     cls: 'form-hidden-field'
12871                 },
12872                 {
12873                     tag: 'ul',
12874                     cls: 'roo-select2-choices',
12875                     cn:[
12876                         {
12877                             tag: 'li',
12878                             cls: 'roo-select2-search-field',
12879                             cn: [
12880                                 buttons
12881                             ]
12882                         }
12883                     ]
12884                 }
12885             ]
12886         };
12887         
12888         var combobox = {
12889             cls: 'roo-select2-container input-group roo-select2-container-multi',
12890             cn: [
12891                 box
12892 //                {
12893 //                    tag: 'ul',
12894 //                    cls: 'typeahead typeahead-long dropdown-menu',
12895 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12896 //                }
12897             ]
12898         };
12899         
12900         if(this.hasFeedback && !this.allowBlank){
12901             
12902             var feedback = {
12903                 tag: 'span',
12904                 cls: 'glyphicon form-control-feedback'
12905             };
12906
12907             combobox.cn.push(feedback);
12908         }
12909         
12910         
12911         if (align ==='left' && this.fieldLabel.length) {
12912             
12913             cfg.cls += ' roo-form-group-label-left';
12914             
12915             cfg.cn = [
12916                 {
12917                     tag : 'i',
12918                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12919                     tooltip : 'This field is required'
12920                 },
12921                 {
12922                     tag: 'label',
12923                     'for' :  id,
12924                     cls : 'control-label',
12925                     html : this.fieldLabel
12926
12927                 },
12928                 {
12929                     cls : "", 
12930                     cn: [
12931                         combobox
12932                     ]
12933                 }
12934
12935             ];
12936             
12937             var labelCfg = cfg.cn[1];
12938             var contentCfg = cfg.cn[2];
12939             
12940
12941             if(this.indicatorpos == 'right'){
12942                 
12943                 cfg.cn = [
12944                     {
12945                         tag: 'label',
12946                         'for' :  id,
12947                         cls : 'control-label',
12948                         cn : [
12949                             {
12950                                 tag : 'span',
12951                                 html : this.fieldLabel
12952                             },
12953                             {
12954                                 tag : 'i',
12955                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12956                                 tooltip : 'This field is required'
12957                             }
12958                         ]
12959                     },
12960                     {
12961                         cls : "",
12962                         cn: [
12963                             combobox
12964                         ]
12965                     }
12966
12967                 ];
12968                 
12969                 
12970                 
12971                 labelCfg = cfg.cn[0];
12972                 contentCfg = cfg.cn[1];
12973             
12974             }
12975             
12976             if(this.labelWidth > 12){
12977                 labelCfg.style = "width: " + this.labelWidth + 'px';
12978             }
12979             
12980             if(this.labelWidth < 13 && this.labelmd == 0){
12981                 this.labelmd = this.labelWidth;
12982             }
12983             
12984             if(this.labellg > 0){
12985                 labelCfg.cls += ' col-lg-' + this.labellg;
12986                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12987             }
12988             
12989             if(this.labelmd > 0){
12990                 labelCfg.cls += ' col-md-' + this.labelmd;
12991                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12992             }
12993             
12994             if(this.labelsm > 0){
12995                 labelCfg.cls += ' col-sm-' + this.labelsm;
12996                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12997             }
12998             
12999             if(this.labelxs > 0){
13000                 labelCfg.cls += ' col-xs-' + this.labelxs;
13001                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13002             }
13003                 
13004                 
13005         } else if ( this.fieldLabel.length) {
13006 //                Roo.log(" label");
13007                  cfg.cn = [
13008                     {
13009                         tag : 'i',
13010                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13011                         tooltip : 'This field is required'
13012                     },
13013                     {
13014                         tag: 'label',
13015                         //cls : 'input-group-addon',
13016                         html : this.fieldLabel
13017                     },
13018                     combobox
13019                 ];
13020                 
13021                 if(this.indicatorpos == 'right'){
13022                     cfg.cn = [
13023                         {
13024                             tag: 'label',
13025                             //cls : 'input-group-addon',
13026                             html : this.fieldLabel
13027                         },
13028                         {
13029                             tag : 'i',
13030                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13031                             tooltip : 'This field is required'
13032                         },
13033                         combobox
13034                     ];
13035                     
13036                 }
13037
13038         } else {
13039             
13040 //                Roo.log(" no label && no align");
13041                 cfg = combobox
13042                      
13043                 
13044         }
13045          
13046         var settings=this;
13047         ['xs','sm','md','lg'].map(function(size){
13048             if (settings[size]) {
13049                 cfg.cls += ' col-' + size + '-' + settings[size];
13050             }
13051         });
13052         
13053         return cfg;
13054         
13055     },
13056     
13057     _initEventsCalled : false,
13058     
13059     // private
13060     initEvents: function()
13061     {   
13062         if (this._initEventsCalled) { // as we call render... prevent looping...
13063             return;
13064         }
13065         this._initEventsCalled = true;
13066         
13067         if (!this.store) {
13068             throw "can not find store for combo";
13069         }
13070         
13071         this.indicator = this.indicatorEl();
13072         
13073         this.store = Roo.factory(this.store, Roo.data);
13074         this.store.parent = this;
13075         
13076         // if we are building from html. then this element is so complex, that we can not really
13077         // use the rendered HTML.
13078         // so we have to trash and replace the previous code.
13079         if (Roo.XComponent.build_from_html) {
13080             // remove this element....
13081             var e = this.el.dom, k=0;
13082             while (e ) { e = e.previousSibling;  ++k;}
13083
13084             this.el.remove();
13085             
13086             this.el=false;
13087             this.rendered = false;
13088             
13089             this.render(this.parent().getChildContainer(true), k);
13090         }
13091         
13092         if(Roo.isIOS && this.useNativeIOS){
13093             this.initIOSView();
13094             return;
13095         }
13096         
13097         /*
13098          * Touch Devices
13099          */
13100         
13101         if(Roo.isTouch && this.mobileTouchView){
13102             this.initTouchView();
13103             return;
13104         }
13105         
13106         if(this.tickable){
13107             this.initTickableEvents();
13108             return;
13109         }
13110         
13111         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13112         
13113         if(this.hiddenName){
13114             
13115             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13116             
13117             this.hiddenField.dom.value =
13118                 this.hiddenValue !== undefined ? this.hiddenValue :
13119                 this.value !== undefined ? this.value : '';
13120
13121             // prevent input submission
13122             this.el.dom.removeAttribute('name');
13123             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13124              
13125              
13126         }
13127         //if(Roo.isGecko){
13128         //    this.el.dom.setAttribute('autocomplete', 'off');
13129         //}
13130         
13131         var cls = 'x-combo-list';
13132         
13133         //this.list = new Roo.Layer({
13134         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13135         //});
13136         
13137         var _this = this;
13138         
13139         (function(){
13140             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13141             _this.list.setWidth(lw);
13142         }).defer(100);
13143         
13144         this.list.on('mouseover', this.onViewOver, this);
13145         this.list.on('mousemove', this.onViewMove, this);
13146         this.list.on('scroll', this.onViewScroll, this);
13147         
13148         /*
13149         this.list.swallowEvent('mousewheel');
13150         this.assetHeight = 0;
13151
13152         if(this.title){
13153             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13154             this.assetHeight += this.header.getHeight();
13155         }
13156
13157         this.innerList = this.list.createChild({cls:cls+'-inner'});
13158         this.innerList.on('mouseover', this.onViewOver, this);
13159         this.innerList.on('mousemove', this.onViewMove, this);
13160         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13161         
13162         if(this.allowBlank && !this.pageSize && !this.disableClear){
13163             this.footer = this.list.createChild({cls:cls+'-ft'});
13164             this.pageTb = new Roo.Toolbar(this.footer);
13165            
13166         }
13167         if(this.pageSize){
13168             this.footer = this.list.createChild({cls:cls+'-ft'});
13169             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13170                     {pageSize: this.pageSize});
13171             
13172         }
13173         
13174         if (this.pageTb && this.allowBlank && !this.disableClear) {
13175             var _this = this;
13176             this.pageTb.add(new Roo.Toolbar.Fill(), {
13177                 cls: 'x-btn-icon x-btn-clear',
13178                 text: '&#160;',
13179                 handler: function()
13180                 {
13181                     _this.collapse();
13182                     _this.clearValue();
13183                     _this.onSelect(false, -1);
13184                 }
13185             });
13186         }
13187         if (this.footer) {
13188             this.assetHeight += this.footer.getHeight();
13189         }
13190         */
13191             
13192         if(!this.tpl){
13193             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13194         }
13195
13196         this.view = new Roo.View(this.list, this.tpl, {
13197             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13198         });
13199         //this.view.wrapEl.setDisplayed(false);
13200         this.view.on('click', this.onViewClick, this);
13201         
13202         
13203         this.store.on('beforeload', this.onBeforeLoad, this);
13204         this.store.on('load', this.onLoad, this);
13205         this.store.on('loadexception', this.onLoadException, this);
13206         /*
13207         if(this.resizable){
13208             this.resizer = new Roo.Resizable(this.list,  {
13209                pinned:true, handles:'se'
13210             });
13211             this.resizer.on('resize', function(r, w, h){
13212                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13213                 this.listWidth = w;
13214                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13215                 this.restrictHeight();
13216             }, this);
13217             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13218         }
13219         */
13220         if(!this.editable){
13221             this.editable = true;
13222             this.setEditable(false);
13223         }
13224         
13225         /*
13226         
13227         if (typeof(this.events.add.listeners) != 'undefined') {
13228             
13229             this.addicon = this.wrap.createChild(
13230                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13231        
13232             this.addicon.on('click', function(e) {
13233                 this.fireEvent('add', this);
13234             }, this);
13235         }
13236         if (typeof(this.events.edit.listeners) != 'undefined') {
13237             
13238             this.editicon = this.wrap.createChild(
13239                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13240             if (this.addicon) {
13241                 this.editicon.setStyle('margin-left', '40px');
13242             }
13243             this.editicon.on('click', function(e) {
13244                 
13245                 // we fire even  if inothing is selected..
13246                 this.fireEvent('edit', this, this.lastData );
13247                 
13248             }, this);
13249         }
13250         */
13251         
13252         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13253             "up" : function(e){
13254                 this.inKeyMode = true;
13255                 this.selectPrev();
13256             },
13257
13258             "down" : function(e){
13259                 if(!this.isExpanded()){
13260                     this.onTriggerClick();
13261                 }else{
13262                     this.inKeyMode = true;
13263                     this.selectNext();
13264                 }
13265             },
13266
13267             "enter" : function(e){
13268 //                this.onViewClick();
13269                 //return true;
13270                 this.collapse();
13271                 
13272                 if(this.fireEvent("specialkey", this, e)){
13273                     this.onViewClick(false);
13274                 }
13275                 
13276                 return true;
13277             },
13278
13279             "esc" : function(e){
13280                 this.collapse();
13281             },
13282
13283             "tab" : function(e){
13284                 this.collapse();
13285                 
13286                 if(this.fireEvent("specialkey", this, e)){
13287                     this.onViewClick(false);
13288                 }
13289                 
13290                 return true;
13291             },
13292
13293             scope : this,
13294
13295             doRelay : function(foo, bar, hname){
13296                 if(hname == 'down' || this.scope.isExpanded()){
13297                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13298                 }
13299                 return true;
13300             },
13301
13302             forceKeyDown: true
13303         });
13304         
13305         
13306         this.queryDelay = Math.max(this.queryDelay || 10,
13307                 this.mode == 'local' ? 10 : 250);
13308         
13309         
13310         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13311         
13312         if(this.typeAhead){
13313             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13314         }
13315         if(this.editable !== false){
13316             this.inputEl().on("keyup", this.onKeyUp, this);
13317         }
13318         if(this.forceSelection){
13319             this.inputEl().on('blur', this.doForce, this);
13320         }
13321         
13322         if(this.multiple){
13323             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13324             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13325         }
13326     },
13327     
13328     initTickableEvents: function()
13329     {   
13330         this.createList();
13331         
13332         if(this.hiddenName){
13333             
13334             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13335             
13336             this.hiddenField.dom.value =
13337                 this.hiddenValue !== undefined ? this.hiddenValue :
13338                 this.value !== undefined ? this.value : '';
13339
13340             // prevent input submission
13341             this.el.dom.removeAttribute('name');
13342             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13343              
13344              
13345         }
13346         
13347 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13348         
13349         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13350         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13351         if(this.triggerList){
13352             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13353         }
13354          
13355         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13356         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13357         
13358         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13359         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13360         
13361         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13362         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13363         
13364         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13365         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13366         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13367         
13368         this.okBtn.hide();
13369         this.cancelBtn.hide();
13370         
13371         var _this = this;
13372         
13373         (function(){
13374             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13375             _this.list.setWidth(lw);
13376         }).defer(100);
13377         
13378         this.list.on('mouseover', this.onViewOver, this);
13379         this.list.on('mousemove', this.onViewMove, this);
13380         
13381         this.list.on('scroll', this.onViewScroll, this);
13382         
13383         if(!this.tpl){
13384             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>';
13385         }
13386
13387         this.view = new Roo.View(this.list, this.tpl, {
13388             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13389         });
13390         
13391         //this.view.wrapEl.setDisplayed(false);
13392         this.view.on('click', this.onViewClick, this);
13393         
13394         
13395         
13396         this.store.on('beforeload', this.onBeforeLoad, this);
13397         this.store.on('load', this.onLoad, this);
13398         this.store.on('loadexception', this.onLoadException, this);
13399         
13400         if(this.editable){
13401             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13402                 "up" : function(e){
13403                     this.inKeyMode = true;
13404                     this.selectPrev();
13405                 },
13406
13407                 "down" : function(e){
13408                     this.inKeyMode = true;
13409                     this.selectNext();
13410                 },
13411
13412                 "enter" : function(e){
13413                     if(this.fireEvent("specialkey", this, e)){
13414                         this.onViewClick(false);
13415                     }
13416                     
13417                     return true;
13418                 },
13419
13420                 "esc" : function(e){
13421                     this.onTickableFooterButtonClick(e, false, false);
13422                 },
13423
13424                 "tab" : function(e){
13425                     this.fireEvent("specialkey", this, e);
13426                     
13427                     this.onTickableFooterButtonClick(e, false, false);
13428                     
13429                     return true;
13430                 },
13431
13432                 scope : this,
13433
13434                 doRelay : function(e, fn, key){
13435                     if(this.scope.isExpanded()){
13436                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13437                     }
13438                     return true;
13439                 },
13440
13441                 forceKeyDown: true
13442             });
13443         }
13444         
13445         this.queryDelay = Math.max(this.queryDelay || 10,
13446                 this.mode == 'local' ? 10 : 250);
13447         
13448         
13449         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13450         
13451         if(this.typeAhead){
13452             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13453         }
13454         
13455         if(this.editable !== false){
13456             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13457         }
13458         
13459         this.indicator = this.indicatorEl();
13460         
13461         if(this.indicator){
13462             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13463             this.indicator.hide();
13464         }
13465         
13466     },
13467
13468     onDestroy : function(){
13469         if(this.view){
13470             this.view.setStore(null);
13471             this.view.el.removeAllListeners();
13472             this.view.el.remove();
13473             this.view.purgeListeners();
13474         }
13475         if(this.list){
13476             this.list.dom.innerHTML  = '';
13477         }
13478         
13479         if(this.store){
13480             this.store.un('beforeload', this.onBeforeLoad, this);
13481             this.store.un('load', this.onLoad, this);
13482             this.store.un('loadexception', this.onLoadException, this);
13483         }
13484         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13485     },
13486
13487     // private
13488     fireKey : function(e){
13489         if(e.isNavKeyPress() && !this.list.isVisible()){
13490             this.fireEvent("specialkey", this, e);
13491         }
13492     },
13493
13494     // private
13495     onResize: function(w, h){
13496 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13497 //        
13498 //        if(typeof w != 'number'){
13499 //            // we do not handle it!?!?
13500 //            return;
13501 //        }
13502 //        var tw = this.trigger.getWidth();
13503 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13504 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13505 //        var x = w - tw;
13506 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13507 //            
13508 //        //this.trigger.setStyle('left', x+'px');
13509 //        
13510 //        if(this.list && this.listWidth === undefined){
13511 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13512 //            this.list.setWidth(lw);
13513 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13514 //        }
13515         
13516     
13517         
13518     },
13519
13520     /**
13521      * Allow or prevent the user from directly editing the field text.  If false is passed,
13522      * the user will only be able to select from the items defined in the dropdown list.  This method
13523      * is the runtime equivalent of setting the 'editable' config option at config time.
13524      * @param {Boolean} value True to allow the user to directly edit the field text
13525      */
13526     setEditable : function(value){
13527         if(value == this.editable){
13528             return;
13529         }
13530         this.editable = value;
13531         if(!value){
13532             this.inputEl().dom.setAttribute('readOnly', true);
13533             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13534             this.inputEl().addClass('x-combo-noedit');
13535         }else{
13536             this.inputEl().dom.setAttribute('readOnly', false);
13537             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13538             this.inputEl().removeClass('x-combo-noedit');
13539         }
13540     },
13541
13542     // private
13543     
13544     onBeforeLoad : function(combo,opts){
13545         if(!this.hasFocus){
13546             return;
13547         }
13548          if (!opts.add) {
13549             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13550          }
13551         this.restrictHeight();
13552         this.selectedIndex = -1;
13553     },
13554
13555     // private
13556     onLoad : function(){
13557         
13558         this.hasQuery = false;
13559         
13560         if(!this.hasFocus){
13561             return;
13562         }
13563         
13564         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13565             this.loading.hide();
13566         }
13567         
13568         if(this.store.getCount() > 0){
13569             
13570             this.expand();
13571             this.restrictHeight();
13572             if(this.lastQuery == this.allQuery){
13573                 if(this.editable && !this.tickable){
13574                     this.inputEl().dom.select();
13575                 }
13576                 
13577                 if(
13578                     !this.selectByValue(this.value, true) &&
13579                     this.autoFocus && 
13580                     (
13581                         !this.store.lastOptions ||
13582                         typeof(this.store.lastOptions.add) == 'undefined' || 
13583                         this.store.lastOptions.add != true
13584                     )
13585                 ){
13586                     this.select(0, true);
13587                 }
13588             }else{
13589                 if(this.autoFocus){
13590                     this.selectNext();
13591                 }
13592                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13593                     this.taTask.delay(this.typeAheadDelay);
13594                 }
13595             }
13596         }else{
13597             this.onEmptyResults();
13598         }
13599         
13600         //this.el.focus();
13601     },
13602     // private
13603     onLoadException : function()
13604     {
13605         this.hasQuery = false;
13606         
13607         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13608             this.loading.hide();
13609         }
13610         
13611         if(this.tickable && this.editable){
13612             return;
13613         }
13614         
13615         this.collapse();
13616         // only causes errors at present
13617         //Roo.log(this.store.reader.jsonData);
13618         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13619             // fixme
13620             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13621         //}
13622         
13623         
13624     },
13625     // private
13626     onTypeAhead : function(){
13627         if(this.store.getCount() > 0){
13628             var r = this.store.getAt(0);
13629             var newValue = r.data[this.displayField];
13630             var len = newValue.length;
13631             var selStart = this.getRawValue().length;
13632             
13633             if(selStart != len){
13634                 this.setRawValue(newValue);
13635                 this.selectText(selStart, newValue.length);
13636             }
13637         }
13638     },
13639
13640     // private
13641     onSelect : function(record, index){
13642         
13643         if(this.fireEvent('beforeselect', this, record, index) !== false){
13644         
13645             this.setFromData(index > -1 ? record.data : false);
13646             
13647             this.collapse();
13648             this.fireEvent('select', this, record, index);
13649         }
13650     },
13651
13652     /**
13653      * Returns the currently selected field value or empty string if no value is set.
13654      * @return {String} value The selected value
13655      */
13656     getValue : function()
13657     {
13658         if(Roo.isIOS && this.useNativeIOS){
13659             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13660         }
13661         
13662         if(this.multiple){
13663             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13664         }
13665         
13666         if(this.valueField){
13667             return typeof this.value != 'undefined' ? this.value : '';
13668         }else{
13669             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13670         }
13671     },
13672     
13673     getRawValue : function()
13674     {
13675         if(Roo.isIOS && this.useNativeIOS){
13676             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13677         }
13678         
13679         var v = this.inputEl().getValue();
13680         
13681         return v;
13682     },
13683
13684     /**
13685      * Clears any text/value currently set in the field
13686      */
13687     clearValue : function(){
13688         
13689         if(this.hiddenField){
13690             this.hiddenField.dom.value = '';
13691         }
13692         this.value = '';
13693         this.setRawValue('');
13694         this.lastSelectionText = '';
13695         this.lastData = false;
13696         
13697         var close = this.closeTriggerEl();
13698         
13699         if(close){
13700             close.hide();
13701         }
13702         
13703         this.validate();
13704         
13705     },
13706
13707     /**
13708      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13709      * will be displayed in the field.  If the value does not match the data value of an existing item,
13710      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13711      * Otherwise the field will be blank (although the value will still be set).
13712      * @param {String} value The value to match
13713      */
13714     setValue : function(v)
13715     {
13716         if(Roo.isIOS && this.useNativeIOS){
13717             this.setIOSValue(v);
13718             return;
13719         }
13720         
13721         if(this.multiple){
13722             this.syncValue();
13723             return;
13724         }
13725         
13726         var text = v;
13727         if(this.valueField){
13728             var r = this.findRecord(this.valueField, v);
13729             if(r){
13730                 text = r.data[this.displayField];
13731             }else if(this.valueNotFoundText !== undefined){
13732                 text = this.valueNotFoundText;
13733             }
13734         }
13735         this.lastSelectionText = text;
13736         if(this.hiddenField){
13737             this.hiddenField.dom.value = v;
13738         }
13739         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13740         this.value = v;
13741         
13742         var close = this.closeTriggerEl();
13743         
13744         if(close){
13745             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13746         }
13747         
13748         this.validate();
13749     },
13750     /**
13751      * @property {Object} the last set data for the element
13752      */
13753     
13754     lastData : false,
13755     /**
13756      * Sets the value of the field based on a object which is related to the record format for the store.
13757      * @param {Object} value the value to set as. or false on reset?
13758      */
13759     setFromData : function(o){
13760         
13761         if(this.multiple){
13762             this.addItem(o);
13763             return;
13764         }
13765             
13766         var dv = ''; // display value
13767         var vv = ''; // value value..
13768         this.lastData = o;
13769         if (this.displayField) {
13770             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13771         } else {
13772             // this is an error condition!!!
13773             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13774         }
13775         
13776         if(this.valueField){
13777             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13778         }
13779         
13780         var close = this.closeTriggerEl();
13781         
13782         if(close){
13783             if(dv.length || vv * 1 > 0){
13784                 close.show() ;
13785                 this.blockFocus=true;
13786             } else {
13787                 close.hide();
13788             }             
13789         }
13790         
13791         if(this.hiddenField){
13792             this.hiddenField.dom.value = vv;
13793             
13794             this.lastSelectionText = dv;
13795             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13796             this.value = vv;
13797             return;
13798         }
13799         // no hidden field.. - we store the value in 'value', but still display
13800         // display field!!!!
13801         this.lastSelectionText = dv;
13802         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13803         this.value = vv;
13804         
13805         
13806         
13807     },
13808     // private
13809     reset : function(){
13810         // overridden so that last data is reset..
13811         
13812         if(this.multiple){
13813             this.clearItem();
13814             return;
13815         }
13816         
13817         this.setValue(this.originalValue);
13818         //this.clearInvalid();
13819         this.lastData = false;
13820         if (this.view) {
13821             this.view.clearSelections();
13822         }
13823         
13824         this.validate();
13825     },
13826     // private
13827     findRecord : function(prop, value){
13828         var record;
13829         if(this.store.getCount() > 0){
13830             this.store.each(function(r){
13831                 if(r.data[prop] == value){
13832                     record = r;
13833                     return false;
13834                 }
13835                 return true;
13836             });
13837         }
13838         return record;
13839     },
13840     
13841     getName: function()
13842     {
13843         // returns hidden if it's set..
13844         if (!this.rendered) {return ''};
13845         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13846         
13847     },
13848     // private
13849     onViewMove : function(e, t){
13850         this.inKeyMode = false;
13851     },
13852
13853     // private
13854     onViewOver : function(e, t){
13855         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13856             return;
13857         }
13858         var item = this.view.findItemFromChild(t);
13859         
13860         if(item){
13861             var index = this.view.indexOf(item);
13862             this.select(index, false);
13863         }
13864     },
13865
13866     // private
13867     onViewClick : function(view, doFocus, el, e)
13868     {
13869         var index = this.view.getSelectedIndexes()[0];
13870         
13871         var r = this.store.getAt(index);
13872         
13873         if(this.tickable){
13874             
13875             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13876                 return;
13877             }
13878             
13879             var rm = false;
13880             var _this = this;
13881             
13882             Roo.each(this.tickItems, function(v,k){
13883                 
13884                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13885                     Roo.log(v);
13886                     _this.tickItems.splice(k, 1);
13887                     
13888                     if(typeof(e) == 'undefined' && view == false){
13889                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13890                     }
13891                     
13892                     rm = true;
13893                     return;
13894                 }
13895             });
13896             
13897             if(rm){
13898                 return;
13899             }
13900             
13901             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13902                 this.tickItems.push(r.data);
13903             }
13904             
13905             if(typeof(e) == 'undefined' && view == false){
13906                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13907             }
13908                     
13909             return;
13910         }
13911         
13912         if(r){
13913             this.onSelect(r, index);
13914         }
13915         if(doFocus !== false && !this.blockFocus){
13916             this.inputEl().focus();
13917         }
13918     },
13919
13920     // private
13921     restrictHeight : function(){
13922         //this.innerList.dom.style.height = '';
13923         //var inner = this.innerList.dom;
13924         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13925         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13926         //this.list.beginUpdate();
13927         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13928         this.list.alignTo(this.inputEl(), this.listAlign);
13929         this.list.alignTo(this.inputEl(), this.listAlign);
13930         //this.list.endUpdate();
13931     },
13932
13933     // private
13934     onEmptyResults : function(){
13935         
13936         if(this.tickable && this.editable){
13937             this.restrictHeight();
13938             return;
13939         }
13940         
13941         this.collapse();
13942     },
13943
13944     /**
13945      * Returns true if the dropdown list is expanded, else false.
13946      */
13947     isExpanded : function(){
13948         return this.list.isVisible();
13949     },
13950
13951     /**
13952      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13953      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13954      * @param {String} value The data value of the item to select
13955      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13956      * selected item if it is not currently in view (defaults to true)
13957      * @return {Boolean} True if the value matched an item in the list, else false
13958      */
13959     selectByValue : function(v, scrollIntoView){
13960         if(v !== undefined && v !== null){
13961             var r = this.findRecord(this.valueField || this.displayField, v);
13962             if(r){
13963                 this.select(this.store.indexOf(r), scrollIntoView);
13964                 return true;
13965             }
13966         }
13967         return false;
13968     },
13969
13970     /**
13971      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13972      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13973      * @param {Number} index The zero-based index of the list item to select
13974      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13975      * selected item if it is not currently in view (defaults to true)
13976      */
13977     select : function(index, scrollIntoView){
13978         this.selectedIndex = index;
13979         this.view.select(index);
13980         if(scrollIntoView !== false){
13981             var el = this.view.getNode(index);
13982             /*
13983              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13984              */
13985             if(el){
13986                 this.list.scrollChildIntoView(el, false);
13987             }
13988         }
13989     },
13990
13991     // private
13992     selectNext : function(){
13993         var ct = this.store.getCount();
13994         if(ct > 0){
13995             if(this.selectedIndex == -1){
13996                 this.select(0);
13997             }else if(this.selectedIndex < ct-1){
13998                 this.select(this.selectedIndex+1);
13999             }
14000         }
14001     },
14002
14003     // private
14004     selectPrev : function(){
14005         var ct = this.store.getCount();
14006         if(ct > 0){
14007             if(this.selectedIndex == -1){
14008                 this.select(0);
14009             }else if(this.selectedIndex != 0){
14010                 this.select(this.selectedIndex-1);
14011             }
14012         }
14013     },
14014
14015     // private
14016     onKeyUp : function(e){
14017         if(this.editable !== false && !e.isSpecialKey()){
14018             this.lastKey = e.getKey();
14019             this.dqTask.delay(this.queryDelay);
14020         }
14021     },
14022
14023     // private
14024     validateBlur : function(){
14025         return !this.list || !this.list.isVisible();   
14026     },
14027
14028     // private
14029     initQuery : function(){
14030         
14031         var v = this.getRawValue();
14032         
14033         if(this.tickable && this.editable){
14034             v = this.tickableInputEl().getValue();
14035         }
14036         
14037         this.doQuery(v);
14038     },
14039
14040     // private
14041     doForce : function(){
14042         if(this.inputEl().dom.value.length > 0){
14043             this.inputEl().dom.value =
14044                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14045              
14046         }
14047     },
14048
14049     /**
14050      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14051      * query allowing the query action to be canceled if needed.
14052      * @param {String} query The SQL query to execute
14053      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14054      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14055      * saved in the current store (defaults to false)
14056      */
14057     doQuery : function(q, forceAll){
14058         
14059         if(q === undefined || q === null){
14060             q = '';
14061         }
14062         var qe = {
14063             query: q,
14064             forceAll: forceAll,
14065             combo: this,
14066             cancel:false
14067         };
14068         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14069             return false;
14070         }
14071         q = qe.query;
14072         
14073         forceAll = qe.forceAll;
14074         if(forceAll === true || (q.length >= this.minChars)){
14075             
14076             this.hasQuery = true;
14077             
14078             if(this.lastQuery != q || this.alwaysQuery){
14079                 this.lastQuery = q;
14080                 if(this.mode == 'local'){
14081                     this.selectedIndex = -1;
14082                     if(forceAll){
14083                         this.store.clearFilter();
14084                     }else{
14085                         
14086                         if(this.specialFilter){
14087                             this.fireEvent('specialfilter', this);
14088                             this.onLoad();
14089                             return;
14090                         }
14091                         
14092                         this.store.filter(this.displayField, q);
14093                     }
14094                     
14095                     this.store.fireEvent("datachanged", this.store);
14096                     
14097                     this.onLoad();
14098                     
14099                     
14100                 }else{
14101                     
14102                     this.store.baseParams[this.queryParam] = q;
14103                     
14104                     var options = {params : this.getParams(q)};
14105                     
14106                     if(this.loadNext){
14107                         options.add = true;
14108                         options.params.start = this.page * this.pageSize;
14109                     }
14110                     
14111                     this.store.load(options);
14112                     
14113                     /*
14114                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14115                      *  we should expand the list on onLoad
14116                      *  so command out it
14117                      */
14118 //                    this.expand();
14119                 }
14120             }else{
14121                 this.selectedIndex = -1;
14122                 this.onLoad();   
14123             }
14124         }
14125         
14126         this.loadNext = false;
14127     },
14128     
14129     // private
14130     getParams : function(q){
14131         var p = {};
14132         //p[this.queryParam] = q;
14133         
14134         if(this.pageSize){
14135             p.start = 0;
14136             p.limit = this.pageSize;
14137         }
14138         return p;
14139     },
14140
14141     /**
14142      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14143      */
14144     collapse : function(){
14145         if(!this.isExpanded()){
14146             return;
14147         }
14148         
14149         this.list.hide();
14150         
14151         this.hasFocus = false;
14152         
14153         if(this.tickable){
14154             this.okBtn.hide();
14155             this.cancelBtn.hide();
14156             this.trigger.show();
14157             
14158             if(this.editable){
14159                 this.tickableInputEl().dom.value = '';
14160                 this.tickableInputEl().blur();
14161             }
14162             
14163         }
14164         
14165         Roo.get(document).un('mousedown', this.collapseIf, this);
14166         Roo.get(document).un('mousewheel', this.collapseIf, this);
14167         if (!this.editable) {
14168             Roo.get(document).un('keydown', this.listKeyPress, this);
14169         }
14170         this.fireEvent('collapse', this);
14171         
14172         this.validate();
14173     },
14174
14175     // private
14176     collapseIf : function(e){
14177         var in_combo  = e.within(this.el);
14178         var in_list =  e.within(this.list);
14179         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14180         
14181         if (in_combo || in_list || is_list) {
14182             //e.stopPropagation();
14183             return;
14184         }
14185         
14186         if(this.tickable){
14187             this.onTickableFooterButtonClick(e, false, false);
14188         }
14189
14190         this.collapse();
14191         
14192     },
14193
14194     /**
14195      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14196      */
14197     expand : function(){
14198        
14199         if(this.isExpanded() || !this.hasFocus){
14200             return;
14201         }
14202         
14203         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14204         this.list.setWidth(lw);
14205         
14206         Roo.log('expand');
14207         
14208         this.list.show();
14209         
14210         this.restrictHeight();
14211         
14212         if(this.tickable){
14213             
14214             this.tickItems = Roo.apply([], this.item);
14215             
14216             this.okBtn.show();
14217             this.cancelBtn.show();
14218             this.trigger.hide();
14219             
14220             if(this.editable){
14221                 this.tickableInputEl().focus();
14222             }
14223             
14224         }
14225         
14226         Roo.get(document).on('mousedown', this.collapseIf, this);
14227         Roo.get(document).on('mousewheel', this.collapseIf, this);
14228         if (!this.editable) {
14229             Roo.get(document).on('keydown', this.listKeyPress, this);
14230         }
14231         
14232         this.fireEvent('expand', this);
14233     },
14234
14235     // private
14236     // Implements the default empty TriggerField.onTriggerClick function
14237     onTriggerClick : function(e)
14238     {
14239         Roo.log('trigger click');
14240         
14241         if(this.disabled || !this.triggerList){
14242             return;
14243         }
14244         
14245         this.page = 0;
14246         this.loadNext = false;
14247         
14248         if(this.isExpanded()){
14249             this.collapse();
14250             if (!this.blockFocus) {
14251                 this.inputEl().focus();
14252             }
14253             
14254         }else {
14255             this.hasFocus = true;
14256             if(this.triggerAction == 'all') {
14257                 this.doQuery(this.allQuery, true);
14258             } else {
14259                 this.doQuery(this.getRawValue());
14260             }
14261             if (!this.blockFocus) {
14262                 this.inputEl().focus();
14263             }
14264         }
14265     },
14266     
14267     onTickableTriggerClick : function(e)
14268     {
14269         if(this.disabled){
14270             return;
14271         }
14272         
14273         this.page = 0;
14274         this.loadNext = false;
14275         this.hasFocus = true;
14276         
14277         if(this.triggerAction == 'all') {
14278             this.doQuery(this.allQuery, true);
14279         } else {
14280             this.doQuery(this.getRawValue());
14281         }
14282     },
14283     
14284     onSearchFieldClick : function(e)
14285     {
14286         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14287             this.onTickableFooterButtonClick(e, false, false);
14288             return;
14289         }
14290         
14291         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14292             return;
14293         }
14294         
14295         this.page = 0;
14296         this.loadNext = false;
14297         this.hasFocus = true;
14298         
14299         if(this.triggerAction == 'all') {
14300             this.doQuery(this.allQuery, true);
14301         } else {
14302             this.doQuery(this.getRawValue());
14303         }
14304     },
14305     
14306     listKeyPress : function(e)
14307     {
14308         //Roo.log('listkeypress');
14309         // scroll to first matching element based on key pres..
14310         if (e.isSpecialKey()) {
14311             return false;
14312         }
14313         var k = String.fromCharCode(e.getKey()).toUpperCase();
14314         //Roo.log(k);
14315         var match  = false;
14316         var csel = this.view.getSelectedNodes();
14317         var cselitem = false;
14318         if (csel.length) {
14319             var ix = this.view.indexOf(csel[0]);
14320             cselitem  = this.store.getAt(ix);
14321             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14322                 cselitem = false;
14323             }
14324             
14325         }
14326         
14327         this.store.each(function(v) { 
14328             if (cselitem) {
14329                 // start at existing selection.
14330                 if (cselitem.id == v.id) {
14331                     cselitem = false;
14332                 }
14333                 return true;
14334             }
14335                 
14336             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14337                 match = this.store.indexOf(v);
14338                 return false;
14339             }
14340             return true;
14341         }, this);
14342         
14343         if (match === false) {
14344             return true; // no more action?
14345         }
14346         // scroll to?
14347         this.view.select(match);
14348         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14349         sn.scrollIntoView(sn.dom.parentNode, false);
14350     },
14351     
14352     onViewScroll : function(e, t){
14353         
14354         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){
14355             return;
14356         }
14357         
14358         this.hasQuery = true;
14359         
14360         this.loading = this.list.select('.loading', true).first();
14361         
14362         if(this.loading === null){
14363             this.list.createChild({
14364                 tag: 'div',
14365                 cls: 'loading roo-select2-more-results roo-select2-active',
14366                 html: 'Loading more results...'
14367             });
14368             
14369             this.loading = this.list.select('.loading', true).first();
14370             
14371             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14372             
14373             this.loading.hide();
14374         }
14375         
14376         this.loading.show();
14377         
14378         var _combo = this;
14379         
14380         this.page++;
14381         this.loadNext = true;
14382         
14383         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14384         
14385         return;
14386     },
14387     
14388     addItem : function(o)
14389     {   
14390         var dv = ''; // display value
14391         
14392         if (this.displayField) {
14393             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14394         } else {
14395             // this is an error condition!!!
14396             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14397         }
14398         
14399         if(!dv.length){
14400             return;
14401         }
14402         
14403         var choice = this.choices.createChild({
14404             tag: 'li',
14405             cls: 'roo-select2-search-choice',
14406             cn: [
14407                 {
14408                     tag: 'div',
14409                     html: dv
14410                 },
14411                 {
14412                     tag: 'a',
14413                     href: '#',
14414                     cls: 'roo-select2-search-choice-close fa fa-times',
14415                     tabindex: '-1'
14416                 }
14417             ]
14418             
14419         }, this.searchField);
14420         
14421         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14422         
14423         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14424         
14425         this.item.push(o);
14426         
14427         this.lastData = o;
14428         
14429         this.syncValue();
14430         
14431         this.inputEl().dom.value = '';
14432         
14433         this.validate();
14434     },
14435     
14436     onRemoveItem : function(e, _self, o)
14437     {
14438         e.preventDefault();
14439         
14440         this.lastItem = Roo.apply([], this.item);
14441         
14442         var index = this.item.indexOf(o.data) * 1;
14443         
14444         if( index < 0){
14445             Roo.log('not this item?!');
14446             return;
14447         }
14448         
14449         this.item.splice(index, 1);
14450         o.item.remove();
14451         
14452         this.syncValue();
14453         
14454         this.fireEvent('remove', this, e);
14455         
14456         this.validate();
14457         
14458     },
14459     
14460     syncValue : function()
14461     {
14462         if(!this.item.length){
14463             this.clearValue();
14464             return;
14465         }
14466             
14467         var value = [];
14468         var _this = this;
14469         Roo.each(this.item, function(i){
14470             if(_this.valueField){
14471                 value.push(i[_this.valueField]);
14472                 return;
14473             }
14474
14475             value.push(i);
14476         });
14477
14478         this.value = value.join(',');
14479
14480         if(this.hiddenField){
14481             this.hiddenField.dom.value = this.value;
14482         }
14483         
14484         this.store.fireEvent("datachanged", this.store);
14485         
14486         this.validate();
14487     },
14488     
14489     clearItem : function()
14490     {
14491         if(!this.multiple){
14492             return;
14493         }
14494         
14495         this.item = [];
14496         
14497         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14498            c.remove();
14499         });
14500         
14501         this.syncValue();
14502         
14503         this.validate();
14504         
14505         if(this.tickable && !Roo.isTouch){
14506             this.view.refresh();
14507         }
14508     },
14509     
14510     inputEl: function ()
14511     {
14512         if(Roo.isIOS && this.useNativeIOS){
14513             return this.el.select('select.roo-ios-select', true).first();
14514         }
14515         
14516         if(Roo.isTouch && this.mobileTouchView){
14517             return this.el.select('input.form-control',true).first();
14518         }
14519         
14520         if(this.tickable){
14521             return this.searchField;
14522         }
14523         
14524         return this.el.select('input.form-control',true).first();
14525     },
14526     
14527     onTickableFooterButtonClick : function(e, btn, el)
14528     {
14529         e.preventDefault();
14530         
14531         this.lastItem = Roo.apply([], this.item);
14532         
14533         if(btn && btn.name == 'cancel'){
14534             this.tickItems = Roo.apply([], this.item);
14535             this.collapse();
14536             return;
14537         }
14538         
14539         this.clearItem();
14540         
14541         var _this = this;
14542         
14543         Roo.each(this.tickItems, function(o){
14544             _this.addItem(o);
14545         });
14546         
14547         this.collapse();
14548         
14549     },
14550     
14551     validate : function()
14552     {
14553         var v = this.getRawValue();
14554         
14555         if(this.multiple){
14556             v = this.getValue();
14557         }
14558         
14559         if(this.disabled || this.allowBlank || v.length){
14560             this.markValid();
14561             return true;
14562         }
14563         
14564         this.markInvalid();
14565         return false;
14566     },
14567     
14568     tickableInputEl : function()
14569     {
14570         if(!this.tickable || !this.editable){
14571             return this.inputEl();
14572         }
14573         
14574         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14575     },
14576     
14577     
14578     getAutoCreateTouchView : function()
14579     {
14580         var id = Roo.id();
14581         
14582         var cfg = {
14583             cls: 'form-group' //input-group
14584         };
14585         
14586         var input =  {
14587             tag: 'input',
14588             id : id,
14589             type : this.inputType,
14590             cls : 'form-control x-combo-noedit',
14591             autocomplete: 'new-password',
14592             placeholder : this.placeholder || '',
14593             readonly : true
14594         };
14595         
14596         if (this.name) {
14597             input.name = this.name;
14598         }
14599         
14600         if (this.size) {
14601             input.cls += ' input-' + this.size;
14602         }
14603         
14604         if (this.disabled) {
14605             input.disabled = true;
14606         }
14607         
14608         var inputblock = {
14609             cls : '',
14610             cn : [
14611                 input
14612             ]
14613         };
14614         
14615         if(this.before){
14616             inputblock.cls += ' input-group';
14617             
14618             inputblock.cn.unshift({
14619                 tag :'span',
14620                 cls : 'input-group-addon',
14621                 html : this.before
14622             });
14623         }
14624         
14625         if(this.removable && !this.multiple){
14626             inputblock.cls += ' roo-removable';
14627             
14628             inputblock.cn.push({
14629                 tag: 'button',
14630                 html : 'x',
14631                 cls : 'roo-combo-removable-btn close'
14632             });
14633         }
14634
14635         if(this.hasFeedback && !this.allowBlank){
14636             
14637             inputblock.cls += ' has-feedback';
14638             
14639             inputblock.cn.push({
14640                 tag: 'span',
14641                 cls: 'glyphicon form-control-feedback'
14642             });
14643             
14644         }
14645         
14646         if (this.after) {
14647             
14648             inputblock.cls += (this.before) ? '' : ' input-group';
14649             
14650             inputblock.cn.push({
14651                 tag :'span',
14652                 cls : 'input-group-addon',
14653                 html : this.after
14654             });
14655         }
14656
14657         var box = {
14658             tag: 'div',
14659             cn: [
14660                 {
14661                     tag: 'input',
14662                     type : 'hidden',
14663                     cls: 'form-hidden-field'
14664                 },
14665                 inputblock
14666             ]
14667             
14668         };
14669         
14670         if(this.multiple){
14671             box = {
14672                 tag: 'div',
14673                 cn: [
14674                     {
14675                         tag: 'input',
14676                         type : 'hidden',
14677                         cls: 'form-hidden-field'
14678                     },
14679                     {
14680                         tag: 'ul',
14681                         cls: 'roo-select2-choices',
14682                         cn:[
14683                             {
14684                                 tag: 'li',
14685                                 cls: 'roo-select2-search-field',
14686                                 cn: [
14687
14688                                     inputblock
14689                                 ]
14690                             }
14691                         ]
14692                     }
14693                 ]
14694             }
14695         };
14696         
14697         var combobox = {
14698             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14699             cn: [
14700                 box
14701             ]
14702         };
14703         
14704         if(!this.multiple && this.showToggleBtn){
14705             
14706             var caret = {
14707                         tag: 'span',
14708                         cls: 'caret'
14709             };
14710             
14711             if (this.caret != false) {
14712                 caret = {
14713                      tag: 'i',
14714                      cls: 'fa fa-' + this.caret
14715                 };
14716                 
14717             }
14718             
14719             combobox.cn.push({
14720                 tag :'span',
14721                 cls : 'input-group-addon btn dropdown-toggle',
14722                 cn : [
14723                     caret,
14724                     {
14725                         tag: 'span',
14726                         cls: 'combobox-clear',
14727                         cn  : [
14728                             {
14729                                 tag : 'i',
14730                                 cls: 'icon-remove'
14731                             }
14732                         ]
14733                     }
14734                 ]
14735
14736             })
14737         }
14738         
14739         if(this.multiple){
14740             combobox.cls += ' roo-select2-container-multi';
14741         }
14742         
14743         var align = this.labelAlign || this.parentLabelAlign();
14744         
14745         if (align ==='left' && this.fieldLabel.length) {
14746
14747             cfg.cn = [
14748                 {
14749                    tag : 'i',
14750                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14751                    tooltip : 'This field is required'
14752                 },
14753                 {
14754                     tag: 'label',
14755                     cls : 'control-label',
14756                     html : this.fieldLabel
14757
14758                 },
14759                 {
14760                     cls : '', 
14761                     cn: [
14762                         combobox
14763                     ]
14764                 }
14765             ];
14766             
14767             var labelCfg = cfg.cn[1];
14768             var contentCfg = cfg.cn[2];
14769             
14770
14771             if(this.indicatorpos == 'right'){
14772                 cfg.cn = [
14773                     {
14774                         tag: 'label',
14775                         'for' :  id,
14776                         cls : 'control-label',
14777                         cn : [
14778                             {
14779                                 tag : 'span',
14780                                 html : this.fieldLabel
14781                             },
14782                             {
14783                                 tag : 'i',
14784                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14785                                 tooltip : 'This field is required'
14786                             }
14787                         ]
14788                     },
14789                     {
14790                         cls : "",
14791                         cn: [
14792                             combobox
14793                         ]
14794                     }
14795
14796                 ];
14797                 
14798                 labelCfg = cfg.cn[0];
14799                 contentCfg = cfg.cn[1];
14800             }
14801             
14802            
14803             
14804             if(this.labelWidth > 12){
14805                 labelCfg.style = "width: " + this.labelWidth + 'px';
14806             }
14807             
14808             if(this.labelWidth < 13 && this.labelmd == 0){
14809                 this.labelmd = this.labelWidth;
14810             }
14811             
14812             if(this.labellg > 0){
14813                 labelCfg.cls += ' col-lg-' + this.labellg;
14814                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14815             }
14816             
14817             if(this.labelmd > 0){
14818                 labelCfg.cls += ' col-md-' + this.labelmd;
14819                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14820             }
14821             
14822             if(this.labelsm > 0){
14823                 labelCfg.cls += ' col-sm-' + this.labelsm;
14824                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14825             }
14826             
14827             if(this.labelxs > 0){
14828                 labelCfg.cls += ' col-xs-' + this.labelxs;
14829                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14830             }
14831                 
14832                 
14833         } else if ( this.fieldLabel.length) {
14834             cfg.cn = [
14835                 {
14836                    tag : 'i',
14837                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14838                    tooltip : 'This field is required'
14839                 },
14840                 {
14841                     tag: 'label',
14842                     cls : 'control-label',
14843                     html : this.fieldLabel
14844
14845                 },
14846                 {
14847                     cls : '', 
14848                     cn: [
14849                         combobox
14850                     ]
14851                 }
14852             ];
14853             
14854             if(this.indicatorpos == 'right'){
14855                 cfg.cn = [
14856                     {
14857                         tag: 'label',
14858                         cls : 'control-label',
14859                         html : this.fieldLabel,
14860                         cn : [
14861                             {
14862                                tag : 'i',
14863                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14864                                tooltip : 'This field is required'
14865                             }
14866                         ]
14867                     },
14868                     {
14869                         cls : '', 
14870                         cn: [
14871                             combobox
14872                         ]
14873                     }
14874                 ];
14875             }
14876         } else {
14877             cfg.cn = combobox;    
14878         }
14879         
14880         
14881         var settings = this;
14882         
14883         ['xs','sm','md','lg'].map(function(size){
14884             if (settings[size]) {
14885                 cfg.cls += ' col-' + size + '-' + settings[size];
14886             }
14887         });
14888         
14889         return cfg;
14890     },
14891     
14892     initTouchView : function()
14893     {
14894         this.renderTouchView();
14895         
14896         this.touchViewEl.on('scroll', function(){
14897             this.el.dom.scrollTop = 0;
14898         }, this);
14899         
14900         this.originalValue = this.getValue();
14901         
14902         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14903         
14904         this.inputEl().on("click", this.showTouchView, this);
14905         if (this.triggerEl) {
14906             this.triggerEl.on("click", this.showTouchView, this);
14907         }
14908         
14909         
14910         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14911         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14912         
14913         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14914         
14915         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14916         this.store.on('load', this.onTouchViewLoad, this);
14917         this.store.on('loadexception', this.onTouchViewLoadException, this);
14918         
14919         if(this.hiddenName){
14920             
14921             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14922             
14923             this.hiddenField.dom.value =
14924                 this.hiddenValue !== undefined ? this.hiddenValue :
14925                 this.value !== undefined ? this.value : '';
14926         
14927             this.el.dom.removeAttribute('name');
14928             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14929         }
14930         
14931         if(this.multiple){
14932             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14933             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14934         }
14935         
14936         if(this.removable && !this.multiple){
14937             var close = this.closeTriggerEl();
14938             if(close){
14939                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14940                 close.on('click', this.removeBtnClick, this, close);
14941             }
14942         }
14943         /*
14944          * fix the bug in Safari iOS8
14945          */
14946         this.inputEl().on("focus", function(e){
14947             document.activeElement.blur();
14948         }, this);
14949         
14950         return;
14951         
14952         
14953     },
14954     
14955     renderTouchView : function()
14956     {
14957         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14958         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14959         
14960         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14961         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14962         
14963         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14964         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14965         this.touchViewBodyEl.setStyle('overflow', 'auto');
14966         
14967         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14968         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14969         
14970         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14971         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14972         
14973     },
14974     
14975     showTouchView : function()
14976     {
14977         if(this.disabled){
14978             return;
14979         }
14980         
14981         this.touchViewHeaderEl.hide();
14982
14983         if(this.modalTitle.length){
14984             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14985             this.touchViewHeaderEl.show();
14986         }
14987
14988         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14989         this.touchViewEl.show();
14990
14991         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14992         
14993         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14994         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14995
14996         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14997
14998         if(this.modalTitle.length){
14999             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15000         }
15001         
15002         this.touchViewBodyEl.setHeight(bodyHeight);
15003
15004         if(this.animate){
15005             var _this = this;
15006             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15007         }else{
15008             this.touchViewEl.addClass('in');
15009         }
15010
15011         this.doTouchViewQuery();
15012         
15013     },
15014     
15015     hideTouchView : function()
15016     {
15017         this.touchViewEl.removeClass('in');
15018
15019         if(this.animate){
15020             var _this = this;
15021             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15022         }else{
15023             this.touchViewEl.setStyle('display', 'none');
15024         }
15025         
15026     },
15027     
15028     setTouchViewValue : function()
15029     {
15030         if(this.multiple){
15031             this.clearItem();
15032         
15033             var _this = this;
15034
15035             Roo.each(this.tickItems, function(o){
15036                 this.addItem(o);
15037             }, this);
15038         }
15039         
15040         this.hideTouchView();
15041     },
15042     
15043     doTouchViewQuery : function()
15044     {
15045         var qe = {
15046             query: '',
15047             forceAll: true,
15048             combo: this,
15049             cancel:false
15050         };
15051         
15052         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15053             return false;
15054         }
15055         
15056         if(!this.alwaysQuery || this.mode == 'local'){
15057             this.onTouchViewLoad();
15058             return;
15059         }
15060         
15061         this.store.load();
15062     },
15063     
15064     onTouchViewBeforeLoad : function(combo,opts)
15065     {
15066         return;
15067     },
15068
15069     // private
15070     onTouchViewLoad : function()
15071     {
15072         if(this.store.getCount() < 1){
15073             this.onTouchViewEmptyResults();
15074             return;
15075         }
15076         
15077         this.clearTouchView();
15078         
15079         var rawValue = this.getRawValue();
15080         
15081         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15082         
15083         this.tickItems = [];
15084         
15085         this.store.data.each(function(d, rowIndex){
15086             var row = this.touchViewListGroup.createChild(template);
15087             
15088             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15089                 row.addClass(d.data.cls);
15090             }
15091             
15092             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15093                 var cfg = {
15094                     data : d.data,
15095                     html : d.data[this.displayField]
15096                 };
15097                 
15098                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15099                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15100                 }
15101             }
15102             row.removeClass('selected');
15103             if(!this.multiple && this.valueField &&
15104                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15105             {
15106                 // radio buttons..
15107                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15108                 row.addClass('selected');
15109             }
15110             
15111             if(this.multiple && this.valueField &&
15112                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15113             {
15114                 
15115                 // checkboxes...
15116                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15117                 this.tickItems.push(d.data);
15118             }
15119             
15120             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15121             
15122         }, this);
15123         
15124         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15125         
15126         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15127
15128         if(this.modalTitle.length){
15129             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15130         }
15131
15132         var listHeight = this.touchViewListGroup.getHeight();
15133         
15134         var _this = this;
15135         
15136         if(firstChecked && listHeight > bodyHeight){
15137             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15138         }
15139         
15140     },
15141     
15142     onTouchViewLoadException : function()
15143     {
15144         this.hideTouchView();
15145     },
15146     
15147     onTouchViewEmptyResults : function()
15148     {
15149         this.clearTouchView();
15150         
15151         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15152         
15153         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15154         
15155     },
15156     
15157     clearTouchView : function()
15158     {
15159         this.touchViewListGroup.dom.innerHTML = '';
15160     },
15161     
15162     onTouchViewClick : function(e, el, o)
15163     {
15164         e.preventDefault();
15165         
15166         var row = o.row;
15167         var rowIndex = o.rowIndex;
15168         
15169         var r = this.store.getAt(rowIndex);
15170         
15171         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15172             
15173             if(!this.multiple){
15174                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15175                     c.dom.removeAttribute('checked');
15176                 }, this);
15177
15178                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15179
15180                 this.setFromData(r.data);
15181
15182                 var close = this.closeTriggerEl();
15183
15184                 if(close){
15185                     close.show();
15186                 }
15187
15188                 this.hideTouchView();
15189
15190                 this.fireEvent('select', this, r, rowIndex);
15191
15192                 return;
15193             }
15194
15195             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15196                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15197                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15198                 return;
15199             }
15200
15201             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15202             this.addItem(r.data);
15203             this.tickItems.push(r.data);
15204         }
15205     },
15206     
15207     getAutoCreateNativeIOS : function()
15208     {
15209         var cfg = {
15210             cls: 'form-group' //input-group,
15211         };
15212         
15213         var combobox =  {
15214             tag: 'select',
15215             cls : 'roo-ios-select'
15216         };
15217         
15218         if (this.name) {
15219             combobox.name = this.name;
15220         }
15221         
15222         if (this.disabled) {
15223             combobox.disabled = true;
15224         }
15225         
15226         var settings = this;
15227         
15228         ['xs','sm','md','lg'].map(function(size){
15229             if (settings[size]) {
15230                 cfg.cls += ' col-' + size + '-' + settings[size];
15231             }
15232         });
15233         
15234         cfg.cn = combobox;
15235         
15236         return cfg;
15237         
15238     },
15239     
15240     initIOSView : function()
15241     {
15242         this.store.on('load', this.onIOSViewLoad, this);
15243         
15244         return;
15245     },
15246     
15247     onIOSViewLoad : function()
15248     {
15249         if(this.store.getCount() < 1){
15250             return;
15251         }
15252         
15253         this.clearIOSView();
15254         
15255         if(this.allowBlank) {
15256             
15257             var default_text = '-- SELECT --';
15258             
15259             if(this.placeholder.length){
15260                 default_text = this.placeholder;
15261             }
15262             
15263             if(this.emptyTitle.length){
15264                 default_text += ' - ' + this.emptyTitle + ' -';
15265             }
15266             
15267             var opt = this.inputEl().createChild({
15268                 tag: 'option',
15269                 value : 0,
15270                 html : default_text
15271             });
15272             
15273             var o = {};
15274             o[this.valueField] = 0;
15275             o[this.displayField] = default_text;
15276             
15277             this.ios_options.push({
15278                 data : o,
15279                 el : opt
15280             });
15281             
15282         }
15283         
15284         this.store.data.each(function(d, rowIndex){
15285             
15286             var html = '';
15287             
15288             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15289                 html = d.data[this.displayField];
15290             }
15291             
15292             var value = '';
15293             
15294             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15295                 value = d.data[this.valueField];
15296             }
15297             
15298             var option = {
15299                 tag: 'option',
15300                 value : value,
15301                 html : html
15302             };
15303             
15304             if(this.value == d.data[this.valueField]){
15305                 option['selected'] = true;
15306             }
15307             
15308             var opt = this.inputEl().createChild(option);
15309             
15310             this.ios_options.push({
15311                 data : d.data,
15312                 el : opt
15313             });
15314             
15315         }, this);
15316         
15317         this.inputEl().on('change', function(){
15318            this.fireEvent('select', this);
15319         }, this);
15320         
15321     },
15322     
15323     clearIOSView: function()
15324     {
15325         this.inputEl().dom.innerHTML = '';
15326         
15327         this.ios_options = [];
15328     },
15329     
15330     setIOSValue: function(v)
15331     {
15332         this.value = v;
15333         
15334         if(!this.ios_options){
15335             return;
15336         }
15337         
15338         Roo.each(this.ios_options, function(opts){
15339            
15340            opts.el.dom.removeAttribute('selected');
15341            
15342            if(opts.data[this.valueField] != v){
15343                return;
15344            }
15345            
15346            opts.el.dom.setAttribute('selected', true);
15347            
15348         }, this);
15349     }
15350
15351     /** 
15352     * @cfg {Boolean} grow 
15353     * @hide 
15354     */
15355     /** 
15356     * @cfg {Number} growMin 
15357     * @hide 
15358     */
15359     /** 
15360     * @cfg {Number} growMax 
15361     * @hide 
15362     */
15363     /**
15364      * @hide
15365      * @method autoSize
15366      */
15367 });
15368
15369 Roo.apply(Roo.bootstrap.ComboBox,  {
15370     
15371     header : {
15372         tag: 'div',
15373         cls: 'modal-header',
15374         cn: [
15375             {
15376                 tag: 'h4',
15377                 cls: 'modal-title'
15378             }
15379         ]
15380     },
15381     
15382     body : {
15383         tag: 'div',
15384         cls: 'modal-body',
15385         cn: [
15386             {
15387                 tag: 'ul',
15388                 cls: 'list-group'
15389             }
15390         ]
15391     },
15392     
15393     listItemRadio : {
15394         tag: 'li',
15395         cls: 'list-group-item',
15396         cn: [
15397             {
15398                 tag: 'span',
15399                 cls: 'roo-combobox-list-group-item-value'
15400             },
15401             {
15402                 tag: 'div',
15403                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15404                 cn: [
15405                     {
15406                         tag: 'input',
15407                         type: 'radio'
15408                     },
15409                     {
15410                         tag: 'label'
15411                     }
15412                 ]
15413             }
15414         ]
15415     },
15416     
15417     listItemCheckbox : {
15418         tag: 'li',
15419         cls: 'list-group-item',
15420         cn: [
15421             {
15422                 tag: 'span',
15423                 cls: 'roo-combobox-list-group-item-value'
15424             },
15425             {
15426                 tag: 'div',
15427                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15428                 cn: [
15429                     {
15430                         tag: 'input',
15431                         type: 'checkbox'
15432                     },
15433                     {
15434                         tag: 'label'
15435                     }
15436                 ]
15437             }
15438         ]
15439     },
15440     
15441     emptyResult : {
15442         tag: 'div',
15443         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15444     },
15445     
15446     footer : {
15447         tag: 'div',
15448         cls: 'modal-footer',
15449         cn: [
15450             {
15451                 tag: 'div',
15452                 cls: 'row',
15453                 cn: [
15454                     {
15455                         tag: 'div',
15456                         cls: 'col-xs-6 text-left',
15457                         cn: {
15458                             tag: 'button',
15459                             cls: 'btn btn-danger roo-touch-view-cancel',
15460                             html: 'Cancel'
15461                         }
15462                     },
15463                     {
15464                         tag: 'div',
15465                         cls: 'col-xs-6 text-right',
15466                         cn: {
15467                             tag: 'button',
15468                             cls: 'btn btn-success roo-touch-view-ok',
15469                             html: 'OK'
15470                         }
15471                     }
15472                 ]
15473             }
15474         ]
15475         
15476     }
15477 });
15478
15479 Roo.apply(Roo.bootstrap.ComboBox,  {
15480     
15481     touchViewTemplate : {
15482         tag: 'div',
15483         cls: 'modal fade roo-combobox-touch-view',
15484         cn: [
15485             {
15486                 tag: 'div',
15487                 cls: 'modal-dialog',
15488                 style : 'position:fixed', // we have to fix position....
15489                 cn: [
15490                     {
15491                         tag: 'div',
15492                         cls: 'modal-content',
15493                         cn: [
15494                             Roo.bootstrap.ComboBox.header,
15495                             Roo.bootstrap.ComboBox.body,
15496                             Roo.bootstrap.ComboBox.footer
15497                         ]
15498                     }
15499                 ]
15500             }
15501         ]
15502     }
15503 });/*
15504  * Based on:
15505  * Ext JS Library 1.1.1
15506  * Copyright(c) 2006-2007, Ext JS, LLC.
15507  *
15508  * Originally Released Under LGPL - original licence link has changed is not relivant.
15509  *
15510  * Fork - LGPL
15511  * <script type="text/javascript">
15512  */
15513
15514 /**
15515  * @class Roo.View
15516  * @extends Roo.util.Observable
15517  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15518  * This class also supports single and multi selection modes. <br>
15519  * Create a data model bound view:
15520  <pre><code>
15521  var store = new Roo.data.Store(...);
15522
15523  var view = new Roo.View({
15524     el : "my-element",
15525     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15526  
15527     singleSelect: true,
15528     selectedClass: "ydataview-selected",
15529     store: store
15530  });
15531
15532  // listen for node click?
15533  view.on("click", function(vw, index, node, e){
15534  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15535  });
15536
15537  // load XML data
15538  dataModel.load("foobar.xml");
15539  </code></pre>
15540  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15541  * <br><br>
15542  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15543  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15544  * 
15545  * Note: old style constructor is still suported (container, template, config)
15546  * 
15547  * @constructor
15548  * Create a new View
15549  * @param {Object} config The config object
15550  * 
15551  */
15552 Roo.View = function(config, depreciated_tpl, depreciated_config){
15553     
15554     this.parent = false;
15555     
15556     if (typeof(depreciated_tpl) == 'undefined') {
15557         // new way.. - universal constructor.
15558         Roo.apply(this, config);
15559         this.el  = Roo.get(this.el);
15560     } else {
15561         // old format..
15562         this.el  = Roo.get(config);
15563         this.tpl = depreciated_tpl;
15564         Roo.apply(this, depreciated_config);
15565     }
15566     this.wrapEl  = this.el.wrap().wrap();
15567     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15568     
15569     
15570     if(typeof(this.tpl) == "string"){
15571         this.tpl = new Roo.Template(this.tpl);
15572     } else {
15573         // support xtype ctors..
15574         this.tpl = new Roo.factory(this.tpl, Roo);
15575     }
15576     
15577     
15578     this.tpl.compile();
15579     
15580     /** @private */
15581     this.addEvents({
15582         /**
15583          * @event beforeclick
15584          * Fires before a click is processed. Returns false to cancel the default action.
15585          * @param {Roo.View} this
15586          * @param {Number} index The index of the target node
15587          * @param {HTMLElement} node The target node
15588          * @param {Roo.EventObject} e The raw event object
15589          */
15590             "beforeclick" : true,
15591         /**
15592          * @event click
15593          * Fires when a template node is clicked.
15594          * @param {Roo.View} this
15595          * @param {Number} index The index of the target node
15596          * @param {HTMLElement} node The target node
15597          * @param {Roo.EventObject} e The raw event object
15598          */
15599             "click" : true,
15600         /**
15601          * @event dblclick
15602          * Fires when a template node is double clicked.
15603          * @param {Roo.View} this
15604          * @param {Number} index The index of the target node
15605          * @param {HTMLElement} node The target node
15606          * @param {Roo.EventObject} e The raw event object
15607          */
15608             "dblclick" : true,
15609         /**
15610          * @event contextmenu
15611          * Fires when a template node is right clicked.
15612          * @param {Roo.View} this
15613          * @param {Number} index The index of the target node
15614          * @param {HTMLElement} node The target node
15615          * @param {Roo.EventObject} e The raw event object
15616          */
15617             "contextmenu" : true,
15618         /**
15619          * @event selectionchange
15620          * Fires when the selected nodes change.
15621          * @param {Roo.View} this
15622          * @param {Array} selections Array of the selected nodes
15623          */
15624             "selectionchange" : true,
15625     
15626         /**
15627          * @event beforeselect
15628          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15629          * @param {Roo.View} this
15630          * @param {HTMLElement} node The node to be selected
15631          * @param {Array} selections Array of currently selected nodes
15632          */
15633             "beforeselect" : true,
15634         /**
15635          * @event preparedata
15636          * Fires on every row to render, to allow you to change the data.
15637          * @param {Roo.View} this
15638          * @param {Object} data to be rendered (change this)
15639          */
15640           "preparedata" : true
15641           
15642           
15643         });
15644
15645
15646
15647     this.el.on({
15648         "click": this.onClick,
15649         "dblclick": this.onDblClick,
15650         "contextmenu": this.onContextMenu,
15651         scope:this
15652     });
15653
15654     this.selections = [];
15655     this.nodes = [];
15656     this.cmp = new Roo.CompositeElementLite([]);
15657     if(this.store){
15658         this.store = Roo.factory(this.store, Roo.data);
15659         this.setStore(this.store, true);
15660     }
15661     
15662     if ( this.footer && this.footer.xtype) {
15663            
15664          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15665         
15666         this.footer.dataSource = this.store;
15667         this.footer.container = fctr;
15668         this.footer = Roo.factory(this.footer, Roo);
15669         fctr.insertFirst(this.el);
15670         
15671         // this is a bit insane - as the paging toolbar seems to detach the el..
15672 //        dom.parentNode.parentNode.parentNode
15673          // they get detached?
15674     }
15675     
15676     
15677     Roo.View.superclass.constructor.call(this);
15678     
15679     
15680 };
15681
15682 Roo.extend(Roo.View, Roo.util.Observable, {
15683     
15684      /**
15685      * @cfg {Roo.data.Store} store Data store to load data from.
15686      */
15687     store : false,
15688     
15689     /**
15690      * @cfg {String|Roo.Element} el The container element.
15691      */
15692     el : '',
15693     
15694     /**
15695      * @cfg {String|Roo.Template} tpl The template used by this View 
15696      */
15697     tpl : false,
15698     /**
15699      * @cfg {String} dataName the named area of the template to use as the data area
15700      *                          Works with domtemplates roo-name="name"
15701      */
15702     dataName: false,
15703     /**
15704      * @cfg {String} selectedClass The css class to add to selected nodes
15705      */
15706     selectedClass : "x-view-selected",
15707      /**
15708      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15709      */
15710     emptyText : "",
15711     
15712     /**
15713      * @cfg {String} text to display on mask (default Loading)
15714      */
15715     mask : false,
15716     /**
15717      * @cfg {Boolean} multiSelect Allow multiple selection
15718      */
15719     multiSelect : false,
15720     /**
15721      * @cfg {Boolean} singleSelect Allow single selection
15722      */
15723     singleSelect:  false,
15724     
15725     /**
15726      * @cfg {Boolean} toggleSelect - selecting 
15727      */
15728     toggleSelect : false,
15729     
15730     /**
15731      * @cfg {Boolean} tickable - selecting 
15732      */
15733     tickable : false,
15734     
15735     /**
15736      * Returns the element this view is bound to.
15737      * @return {Roo.Element}
15738      */
15739     getEl : function(){
15740         return this.wrapEl;
15741     },
15742     
15743     
15744
15745     /**
15746      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15747      */
15748     refresh : function(){
15749         //Roo.log('refresh');
15750         var t = this.tpl;
15751         
15752         // if we are using something like 'domtemplate', then
15753         // the what gets used is:
15754         // t.applySubtemplate(NAME, data, wrapping data..)
15755         // the outer template then get' applied with
15756         //     the store 'extra data'
15757         // and the body get's added to the
15758         //      roo-name="data" node?
15759         //      <span class='roo-tpl-{name}'></span> ?????
15760         
15761         
15762         
15763         this.clearSelections();
15764         this.el.update("");
15765         var html = [];
15766         var records = this.store.getRange();
15767         if(records.length < 1) {
15768             
15769             // is this valid??  = should it render a template??
15770             
15771             this.el.update(this.emptyText);
15772             return;
15773         }
15774         var el = this.el;
15775         if (this.dataName) {
15776             this.el.update(t.apply(this.store.meta)); //????
15777             el = this.el.child('.roo-tpl-' + this.dataName);
15778         }
15779         
15780         for(var i = 0, len = records.length; i < len; i++){
15781             var data = this.prepareData(records[i].data, i, records[i]);
15782             this.fireEvent("preparedata", this, data, i, records[i]);
15783             
15784             var d = Roo.apply({}, data);
15785             
15786             if(this.tickable){
15787                 Roo.apply(d, {'roo-id' : Roo.id()});
15788                 
15789                 var _this = this;
15790             
15791                 Roo.each(this.parent.item, function(item){
15792                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15793                         return;
15794                     }
15795                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15796                 });
15797             }
15798             
15799             html[html.length] = Roo.util.Format.trim(
15800                 this.dataName ?
15801                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15802                     t.apply(d)
15803             );
15804         }
15805         
15806         
15807         
15808         el.update(html.join(""));
15809         this.nodes = el.dom.childNodes;
15810         this.updateIndexes(0);
15811     },
15812     
15813
15814     /**
15815      * Function to override to reformat the data that is sent to
15816      * the template for each node.
15817      * DEPRICATED - use the preparedata event handler.
15818      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15819      * a JSON object for an UpdateManager bound view).
15820      */
15821     prepareData : function(data, index, record)
15822     {
15823         this.fireEvent("preparedata", this, data, index, record);
15824         return data;
15825     },
15826
15827     onUpdate : function(ds, record){
15828         // Roo.log('on update');   
15829         this.clearSelections();
15830         var index = this.store.indexOf(record);
15831         var n = this.nodes[index];
15832         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15833         n.parentNode.removeChild(n);
15834         this.updateIndexes(index, index);
15835     },
15836
15837     
15838     
15839 // --------- FIXME     
15840     onAdd : function(ds, records, index)
15841     {
15842         //Roo.log(['on Add', ds, records, index] );        
15843         this.clearSelections();
15844         if(this.nodes.length == 0){
15845             this.refresh();
15846             return;
15847         }
15848         var n = this.nodes[index];
15849         for(var i = 0, len = records.length; i < len; i++){
15850             var d = this.prepareData(records[i].data, i, records[i]);
15851             if(n){
15852                 this.tpl.insertBefore(n, d);
15853             }else{
15854                 
15855                 this.tpl.append(this.el, d);
15856             }
15857         }
15858         this.updateIndexes(index);
15859     },
15860
15861     onRemove : function(ds, record, index){
15862        // Roo.log('onRemove');
15863         this.clearSelections();
15864         var el = this.dataName  ?
15865             this.el.child('.roo-tpl-' + this.dataName) :
15866             this.el; 
15867         
15868         el.dom.removeChild(this.nodes[index]);
15869         this.updateIndexes(index);
15870     },
15871
15872     /**
15873      * Refresh an individual node.
15874      * @param {Number} index
15875      */
15876     refreshNode : function(index){
15877         this.onUpdate(this.store, this.store.getAt(index));
15878     },
15879
15880     updateIndexes : function(startIndex, endIndex){
15881         var ns = this.nodes;
15882         startIndex = startIndex || 0;
15883         endIndex = endIndex || ns.length - 1;
15884         for(var i = startIndex; i <= endIndex; i++){
15885             ns[i].nodeIndex = i;
15886         }
15887     },
15888
15889     /**
15890      * Changes the data store this view uses and refresh the view.
15891      * @param {Store} store
15892      */
15893     setStore : function(store, initial){
15894         if(!initial && this.store){
15895             this.store.un("datachanged", this.refresh);
15896             this.store.un("add", this.onAdd);
15897             this.store.un("remove", this.onRemove);
15898             this.store.un("update", this.onUpdate);
15899             this.store.un("clear", this.refresh);
15900             this.store.un("beforeload", this.onBeforeLoad);
15901             this.store.un("load", this.onLoad);
15902             this.store.un("loadexception", this.onLoad);
15903         }
15904         if(store){
15905           
15906             store.on("datachanged", this.refresh, this);
15907             store.on("add", this.onAdd, this);
15908             store.on("remove", this.onRemove, this);
15909             store.on("update", this.onUpdate, this);
15910             store.on("clear", this.refresh, this);
15911             store.on("beforeload", this.onBeforeLoad, this);
15912             store.on("load", this.onLoad, this);
15913             store.on("loadexception", this.onLoad, this);
15914         }
15915         
15916         if(store){
15917             this.refresh();
15918         }
15919     },
15920     /**
15921      * onbeforeLoad - masks the loading area.
15922      *
15923      */
15924     onBeforeLoad : function(store,opts)
15925     {
15926          //Roo.log('onBeforeLoad');   
15927         if (!opts.add) {
15928             this.el.update("");
15929         }
15930         this.el.mask(this.mask ? this.mask : "Loading" ); 
15931     },
15932     onLoad : function ()
15933     {
15934         this.el.unmask();
15935     },
15936     
15937
15938     /**
15939      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15940      * @param {HTMLElement} node
15941      * @return {HTMLElement} The template node
15942      */
15943     findItemFromChild : function(node){
15944         var el = this.dataName  ?
15945             this.el.child('.roo-tpl-' + this.dataName,true) :
15946             this.el.dom; 
15947         
15948         if(!node || node.parentNode == el){
15949                     return node;
15950             }
15951             var p = node.parentNode;
15952             while(p && p != el){
15953             if(p.parentNode == el){
15954                 return p;
15955             }
15956             p = p.parentNode;
15957         }
15958             return null;
15959     },
15960
15961     /** @ignore */
15962     onClick : function(e){
15963         var item = this.findItemFromChild(e.getTarget());
15964         if(item){
15965             var index = this.indexOf(item);
15966             if(this.onItemClick(item, index, e) !== false){
15967                 this.fireEvent("click", this, index, item, e);
15968             }
15969         }else{
15970             this.clearSelections();
15971         }
15972     },
15973
15974     /** @ignore */
15975     onContextMenu : function(e){
15976         var item = this.findItemFromChild(e.getTarget());
15977         if(item){
15978             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15979         }
15980     },
15981
15982     /** @ignore */
15983     onDblClick : function(e){
15984         var item = this.findItemFromChild(e.getTarget());
15985         if(item){
15986             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15987         }
15988     },
15989
15990     onItemClick : function(item, index, e)
15991     {
15992         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15993             return false;
15994         }
15995         if (this.toggleSelect) {
15996             var m = this.isSelected(item) ? 'unselect' : 'select';
15997             //Roo.log(m);
15998             var _t = this;
15999             _t[m](item, true, false);
16000             return true;
16001         }
16002         if(this.multiSelect || this.singleSelect){
16003             if(this.multiSelect && e.shiftKey && this.lastSelection){
16004                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16005             }else{
16006                 this.select(item, this.multiSelect && e.ctrlKey);
16007                 this.lastSelection = item;
16008             }
16009             
16010             if(!this.tickable){
16011                 e.preventDefault();
16012             }
16013             
16014         }
16015         return true;
16016     },
16017
16018     /**
16019      * Get the number of selected nodes.
16020      * @return {Number}
16021      */
16022     getSelectionCount : function(){
16023         return this.selections.length;
16024     },
16025
16026     /**
16027      * Get the currently selected nodes.
16028      * @return {Array} An array of HTMLElements
16029      */
16030     getSelectedNodes : function(){
16031         return this.selections;
16032     },
16033
16034     /**
16035      * Get the indexes of the selected nodes.
16036      * @return {Array}
16037      */
16038     getSelectedIndexes : function(){
16039         var indexes = [], s = this.selections;
16040         for(var i = 0, len = s.length; i < len; i++){
16041             indexes.push(s[i].nodeIndex);
16042         }
16043         return indexes;
16044     },
16045
16046     /**
16047      * Clear all selections
16048      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16049      */
16050     clearSelections : function(suppressEvent){
16051         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16052             this.cmp.elements = this.selections;
16053             this.cmp.removeClass(this.selectedClass);
16054             this.selections = [];
16055             if(!suppressEvent){
16056                 this.fireEvent("selectionchange", this, this.selections);
16057             }
16058         }
16059     },
16060
16061     /**
16062      * Returns true if the passed node is selected
16063      * @param {HTMLElement/Number} node The node or node index
16064      * @return {Boolean}
16065      */
16066     isSelected : function(node){
16067         var s = this.selections;
16068         if(s.length < 1){
16069             return false;
16070         }
16071         node = this.getNode(node);
16072         return s.indexOf(node) !== -1;
16073     },
16074
16075     /**
16076      * Selects nodes.
16077      * @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
16078      * @param {Boolean} keepExisting (optional) true to keep existing selections
16079      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16080      */
16081     select : function(nodeInfo, keepExisting, suppressEvent){
16082         if(nodeInfo instanceof Array){
16083             if(!keepExisting){
16084                 this.clearSelections(true);
16085             }
16086             for(var i = 0, len = nodeInfo.length; i < len; i++){
16087                 this.select(nodeInfo[i], true, true);
16088             }
16089             return;
16090         } 
16091         var node = this.getNode(nodeInfo);
16092         if(!node || this.isSelected(node)){
16093             return; // already selected.
16094         }
16095         if(!keepExisting){
16096             this.clearSelections(true);
16097         }
16098         
16099         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16100             Roo.fly(node).addClass(this.selectedClass);
16101             this.selections.push(node);
16102             if(!suppressEvent){
16103                 this.fireEvent("selectionchange", this, this.selections);
16104             }
16105         }
16106         
16107         
16108     },
16109       /**
16110      * Unselects nodes.
16111      * @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
16112      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16113      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16114      */
16115     unselect : function(nodeInfo, keepExisting, suppressEvent)
16116     {
16117         if(nodeInfo instanceof Array){
16118             Roo.each(this.selections, function(s) {
16119                 this.unselect(s, nodeInfo);
16120             }, this);
16121             return;
16122         }
16123         var node = this.getNode(nodeInfo);
16124         if(!node || !this.isSelected(node)){
16125             //Roo.log("not selected");
16126             return; // not selected.
16127         }
16128         // fireevent???
16129         var ns = [];
16130         Roo.each(this.selections, function(s) {
16131             if (s == node ) {
16132                 Roo.fly(node).removeClass(this.selectedClass);
16133
16134                 return;
16135             }
16136             ns.push(s);
16137         },this);
16138         
16139         this.selections= ns;
16140         this.fireEvent("selectionchange", this, this.selections);
16141     },
16142
16143     /**
16144      * Gets a template node.
16145      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16146      * @return {HTMLElement} The node or null if it wasn't found
16147      */
16148     getNode : function(nodeInfo){
16149         if(typeof nodeInfo == "string"){
16150             return document.getElementById(nodeInfo);
16151         }else if(typeof nodeInfo == "number"){
16152             return this.nodes[nodeInfo];
16153         }
16154         return nodeInfo;
16155     },
16156
16157     /**
16158      * Gets a range template nodes.
16159      * @param {Number} startIndex
16160      * @param {Number} endIndex
16161      * @return {Array} An array of nodes
16162      */
16163     getNodes : function(start, end){
16164         var ns = this.nodes;
16165         start = start || 0;
16166         end = typeof end == "undefined" ? ns.length - 1 : end;
16167         var nodes = [];
16168         if(start <= end){
16169             for(var i = start; i <= end; i++){
16170                 nodes.push(ns[i]);
16171             }
16172         } else{
16173             for(var i = start; i >= end; i--){
16174                 nodes.push(ns[i]);
16175             }
16176         }
16177         return nodes;
16178     },
16179
16180     /**
16181      * Finds the index of the passed node
16182      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16183      * @return {Number} The index of the node or -1
16184      */
16185     indexOf : function(node){
16186         node = this.getNode(node);
16187         if(typeof node.nodeIndex == "number"){
16188             return node.nodeIndex;
16189         }
16190         var ns = this.nodes;
16191         for(var i = 0, len = ns.length; i < len; i++){
16192             if(ns[i] == node){
16193                 return i;
16194             }
16195         }
16196         return -1;
16197     }
16198 });
16199 /*
16200  * - LGPL
16201  *
16202  * based on jquery fullcalendar
16203  * 
16204  */
16205
16206 Roo.bootstrap = Roo.bootstrap || {};
16207 /**
16208  * @class Roo.bootstrap.Calendar
16209  * @extends Roo.bootstrap.Component
16210  * Bootstrap Calendar class
16211  * @cfg {Boolean} loadMask (true|false) default false
16212  * @cfg {Object} header generate the user specific header of the calendar, default false
16213
16214  * @constructor
16215  * Create a new Container
16216  * @param {Object} config The config object
16217  */
16218
16219
16220
16221 Roo.bootstrap.Calendar = function(config){
16222     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16223      this.addEvents({
16224         /**
16225              * @event select
16226              * Fires when a date is selected
16227              * @param {DatePicker} this
16228              * @param {Date} date The selected date
16229              */
16230         'select': true,
16231         /**
16232              * @event monthchange
16233              * Fires when the displayed month changes 
16234              * @param {DatePicker} this
16235              * @param {Date} date The selected month
16236              */
16237         'monthchange': true,
16238         /**
16239              * @event evententer
16240              * Fires when mouse over an event
16241              * @param {Calendar} this
16242              * @param {event} Event
16243              */
16244         'evententer': true,
16245         /**
16246              * @event eventleave
16247              * Fires when the mouse leaves an
16248              * @param {Calendar} this
16249              * @param {event}
16250              */
16251         'eventleave': true,
16252         /**
16253              * @event eventclick
16254              * Fires when the mouse click an
16255              * @param {Calendar} this
16256              * @param {event}
16257              */
16258         'eventclick': true
16259         
16260     });
16261
16262 };
16263
16264 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16265     
16266      /**
16267      * @cfg {Number} startDay
16268      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16269      */
16270     startDay : 0,
16271     
16272     loadMask : false,
16273     
16274     header : false,
16275       
16276     getAutoCreate : function(){
16277         
16278         
16279         var fc_button = function(name, corner, style, content ) {
16280             return Roo.apply({},{
16281                 tag : 'span',
16282                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16283                          (corner.length ?
16284                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16285                             ''
16286                         ),
16287                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16288                 unselectable: 'on'
16289             });
16290         };
16291         
16292         var header = {};
16293         
16294         if(!this.header){
16295             header = {
16296                 tag : 'table',
16297                 cls : 'fc-header',
16298                 style : 'width:100%',
16299                 cn : [
16300                     {
16301                         tag: 'tr',
16302                         cn : [
16303                             {
16304                                 tag : 'td',
16305                                 cls : 'fc-header-left',
16306                                 cn : [
16307                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16308                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16309                                     { tag: 'span', cls: 'fc-header-space' },
16310                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16311
16312
16313                                 ]
16314                             },
16315
16316                             {
16317                                 tag : 'td',
16318                                 cls : 'fc-header-center',
16319                                 cn : [
16320                                     {
16321                                         tag: 'span',
16322                                         cls: 'fc-header-title',
16323                                         cn : {
16324                                             tag: 'H2',
16325                                             html : 'month / year'
16326                                         }
16327                                     }
16328
16329                                 ]
16330                             },
16331                             {
16332                                 tag : 'td',
16333                                 cls : 'fc-header-right',
16334                                 cn : [
16335                               /*      fc_button('month', 'left', '', 'month' ),
16336                                     fc_button('week', '', '', 'week' ),
16337                                     fc_button('day', 'right', '', 'day' )
16338                                 */    
16339
16340                                 ]
16341                             }
16342
16343                         ]
16344                     }
16345                 ]
16346             };
16347         }
16348         
16349         header = this.header;
16350         
16351        
16352         var cal_heads = function() {
16353             var ret = [];
16354             // fixme - handle this.
16355             
16356             for (var i =0; i < Date.dayNames.length; i++) {
16357                 var d = Date.dayNames[i];
16358                 ret.push({
16359                     tag: 'th',
16360                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16361                     html : d.substring(0,3)
16362                 });
16363                 
16364             }
16365             ret[0].cls += ' fc-first';
16366             ret[6].cls += ' fc-last';
16367             return ret;
16368         };
16369         var cal_cell = function(n) {
16370             return  {
16371                 tag: 'td',
16372                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16373                 cn : [
16374                     {
16375                         cn : [
16376                             {
16377                                 cls: 'fc-day-number',
16378                                 html: 'D'
16379                             },
16380                             {
16381                                 cls: 'fc-day-content',
16382                              
16383                                 cn : [
16384                                      {
16385                                         style: 'position: relative;' // height: 17px;
16386                                     }
16387                                 ]
16388                             }
16389                             
16390                             
16391                         ]
16392                     }
16393                 ]
16394                 
16395             }
16396         };
16397         var cal_rows = function() {
16398             
16399             var ret = [];
16400             for (var r = 0; r < 6; r++) {
16401                 var row= {
16402                     tag : 'tr',
16403                     cls : 'fc-week',
16404                     cn : []
16405                 };
16406                 
16407                 for (var i =0; i < Date.dayNames.length; i++) {
16408                     var d = Date.dayNames[i];
16409                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16410
16411                 }
16412                 row.cn[0].cls+=' fc-first';
16413                 row.cn[0].cn[0].style = 'min-height:90px';
16414                 row.cn[6].cls+=' fc-last';
16415                 ret.push(row);
16416                 
16417             }
16418             ret[0].cls += ' fc-first';
16419             ret[4].cls += ' fc-prev-last';
16420             ret[5].cls += ' fc-last';
16421             return ret;
16422             
16423         };
16424         
16425         var cal_table = {
16426             tag: 'table',
16427             cls: 'fc-border-separate',
16428             style : 'width:100%',
16429             cellspacing  : 0,
16430             cn : [
16431                 { 
16432                     tag: 'thead',
16433                     cn : [
16434                         { 
16435                             tag: 'tr',
16436                             cls : 'fc-first fc-last',
16437                             cn : cal_heads()
16438                         }
16439                     ]
16440                 },
16441                 { 
16442                     tag: 'tbody',
16443                     cn : cal_rows()
16444                 }
16445                   
16446             ]
16447         };
16448          
16449          var cfg = {
16450             cls : 'fc fc-ltr',
16451             cn : [
16452                 header,
16453                 {
16454                     cls : 'fc-content',
16455                     style : "position: relative;",
16456                     cn : [
16457                         {
16458                             cls : 'fc-view fc-view-month fc-grid',
16459                             style : 'position: relative',
16460                             unselectable : 'on',
16461                             cn : [
16462                                 {
16463                                     cls : 'fc-event-container',
16464                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16465                                 },
16466                                 cal_table
16467                             ]
16468                         }
16469                     ]
16470     
16471                 }
16472            ] 
16473             
16474         };
16475         
16476          
16477         
16478         return cfg;
16479     },
16480     
16481     
16482     initEvents : function()
16483     {
16484         if(!this.store){
16485             throw "can not find store for calendar";
16486         }
16487         
16488         var mark = {
16489             tag: "div",
16490             cls:"x-dlg-mask",
16491             style: "text-align:center",
16492             cn: [
16493                 {
16494                     tag: "div",
16495                     style: "background-color:white;width:50%;margin:250 auto",
16496                     cn: [
16497                         {
16498                             tag: "img",
16499                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16500                         },
16501                         {
16502                             tag: "span",
16503                             html: "Loading"
16504                         }
16505                         
16506                     ]
16507                 }
16508             ]
16509         };
16510         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16511         
16512         var size = this.el.select('.fc-content', true).first().getSize();
16513         this.maskEl.setSize(size.width, size.height);
16514         this.maskEl.enableDisplayMode("block");
16515         if(!this.loadMask){
16516             this.maskEl.hide();
16517         }
16518         
16519         this.store = Roo.factory(this.store, Roo.data);
16520         this.store.on('load', this.onLoad, this);
16521         this.store.on('beforeload', this.onBeforeLoad, this);
16522         
16523         this.resize();
16524         
16525         this.cells = this.el.select('.fc-day',true);
16526         //Roo.log(this.cells);
16527         this.textNodes = this.el.query('.fc-day-number');
16528         this.cells.addClassOnOver('fc-state-hover');
16529         
16530         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16531         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16532         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16533         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16534         
16535         this.on('monthchange', this.onMonthChange, this);
16536         
16537         this.update(new Date().clearTime());
16538     },
16539     
16540     resize : function() {
16541         var sz  = this.el.getSize();
16542         
16543         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16544         this.el.select('.fc-day-content div',true).setHeight(34);
16545     },
16546     
16547     
16548     // private
16549     showPrevMonth : function(e){
16550         this.update(this.activeDate.add("mo", -1));
16551     },
16552     showToday : function(e){
16553         this.update(new Date().clearTime());
16554     },
16555     // private
16556     showNextMonth : function(e){
16557         this.update(this.activeDate.add("mo", 1));
16558     },
16559
16560     // private
16561     showPrevYear : function(){
16562         this.update(this.activeDate.add("y", -1));
16563     },
16564
16565     // private
16566     showNextYear : function(){
16567         this.update(this.activeDate.add("y", 1));
16568     },
16569
16570     
16571    // private
16572     update : function(date)
16573     {
16574         var vd = this.activeDate;
16575         this.activeDate = date;
16576 //        if(vd && this.el){
16577 //            var t = date.getTime();
16578 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16579 //                Roo.log('using add remove');
16580 //                
16581 //                this.fireEvent('monthchange', this, date);
16582 //                
16583 //                this.cells.removeClass("fc-state-highlight");
16584 //                this.cells.each(function(c){
16585 //                   if(c.dateValue == t){
16586 //                       c.addClass("fc-state-highlight");
16587 //                       setTimeout(function(){
16588 //                            try{c.dom.firstChild.focus();}catch(e){}
16589 //                       }, 50);
16590 //                       return false;
16591 //                   }
16592 //                   return true;
16593 //                });
16594 //                return;
16595 //            }
16596 //        }
16597         
16598         var days = date.getDaysInMonth();
16599         
16600         var firstOfMonth = date.getFirstDateOfMonth();
16601         var startingPos = firstOfMonth.getDay()-this.startDay;
16602         
16603         if(startingPos < this.startDay){
16604             startingPos += 7;
16605         }
16606         
16607         var pm = date.add(Date.MONTH, -1);
16608         var prevStart = pm.getDaysInMonth()-startingPos;
16609 //        
16610         this.cells = this.el.select('.fc-day',true);
16611         this.textNodes = this.el.query('.fc-day-number');
16612         this.cells.addClassOnOver('fc-state-hover');
16613         
16614         var cells = this.cells.elements;
16615         var textEls = this.textNodes;
16616         
16617         Roo.each(cells, function(cell){
16618             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16619         });
16620         
16621         days += startingPos;
16622
16623         // convert everything to numbers so it's fast
16624         var day = 86400000;
16625         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16626         //Roo.log(d);
16627         //Roo.log(pm);
16628         //Roo.log(prevStart);
16629         
16630         var today = new Date().clearTime().getTime();
16631         var sel = date.clearTime().getTime();
16632         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16633         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16634         var ddMatch = this.disabledDatesRE;
16635         var ddText = this.disabledDatesText;
16636         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16637         var ddaysText = this.disabledDaysText;
16638         var format = this.format;
16639         
16640         var setCellClass = function(cal, cell){
16641             cell.row = 0;
16642             cell.events = [];
16643             cell.more = [];
16644             //Roo.log('set Cell Class');
16645             cell.title = "";
16646             var t = d.getTime();
16647             
16648             //Roo.log(d);
16649             
16650             cell.dateValue = t;
16651             if(t == today){
16652                 cell.className += " fc-today";
16653                 cell.className += " fc-state-highlight";
16654                 cell.title = cal.todayText;
16655             }
16656             if(t == sel){
16657                 // disable highlight in other month..
16658                 //cell.className += " fc-state-highlight";
16659                 
16660             }
16661             // disabling
16662             if(t < min) {
16663                 cell.className = " fc-state-disabled";
16664                 cell.title = cal.minText;
16665                 return;
16666             }
16667             if(t > max) {
16668                 cell.className = " fc-state-disabled";
16669                 cell.title = cal.maxText;
16670                 return;
16671             }
16672             if(ddays){
16673                 if(ddays.indexOf(d.getDay()) != -1){
16674                     cell.title = ddaysText;
16675                     cell.className = " fc-state-disabled";
16676                 }
16677             }
16678             if(ddMatch && format){
16679                 var fvalue = d.dateFormat(format);
16680                 if(ddMatch.test(fvalue)){
16681                     cell.title = ddText.replace("%0", fvalue);
16682                     cell.className = " fc-state-disabled";
16683                 }
16684             }
16685             
16686             if (!cell.initialClassName) {
16687                 cell.initialClassName = cell.dom.className;
16688             }
16689             
16690             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16691         };
16692
16693         var i = 0;
16694         
16695         for(; i < startingPos; i++) {
16696             textEls[i].innerHTML = (++prevStart);
16697             d.setDate(d.getDate()+1);
16698             
16699             cells[i].className = "fc-past fc-other-month";
16700             setCellClass(this, cells[i]);
16701         }
16702         
16703         var intDay = 0;
16704         
16705         for(; i < days; i++){
16706             intDay = i - startingPos + 1;
16707             textEls[i].innerHTML = (intDay);
16708             d.setDate(d.getDate()+1);
16709             
16710             cells[i].className = ''; // "x-date-active";
16711             setCellClass(this, cells[i]);
16712         }
16713         var extraDays = 0;
16714         
16715         for(; i < 42; i++) {
16716             textEls[i].innerHTML = (++extraDays);
16717             d.setDate(d.getDate()+1);
16718             
16719             cells[i].className = "fc-future fc-other-month";
16720             setCellClass(this, cells[i]);
16721         }
16722         
16723         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16724         
16725         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16726         
16727         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16728         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16729         
16730         if(totalRows != 6){
16731             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16732             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16733         }
16734         
16735         this.fireEvent('monthchange', this, date);
16736         
16737         
16738         /*
16739         if(!this.internalRender){
16740             var main = this.el.dom.firstChild;
16741             var w = main.offsetWidth;
16742             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16743             Roo.fly(main).setWidth(w);
16744             this.internalRender = true;
16745             // opera does not respect the auto grow header center column
16746             // then, after it gets a width opera refuses to recalculate
16747             // without a second pass
16748             if(Roo.isOpera && !this.secondPass){
16749                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16750                 this.secondPass = true;
16751                 this.update.defer(10, this, [date]);
16752             }
16753         }
16754         */
16755         
16756     },
16757     
16758     findCell : function(dt) {
16759         dt = dt.clearTime().getTime();
16760         var ret = false;
16761         this.cells.each(function(c){
16762             //Roo.log("check " +c.dateValue + '?=' + dt);
16763             if(c.dateValue == dt){
16764                 ret = c;
16765                 return false;
16766             }
16767             return true;
16768         });
16769         
16770         return ret;
16771     },
16772     
16773     findCells : function(ev) {
16774         var s = ev.start.clone().clearTime().getTime();
16775        // Roo.log(s);
16776         var e= ev.end.clone().clearTime().getTime();
16777        // Roo.log(e);
16778         var ret = [];
16779         this.cells.each(function(c){
16780              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16781             
16782             if(c.dateValue > e){
16783                 return ;
16784             }
16785             if(c.dateValue < s){
16786                 return ;
16787             }
16788             ret.push(c);
16789         });
16790         
16791         return ret;    
16792     },
16793     
16794 //    findBestRow: function(cells)
16795 //    {
16796 //        var ret = 0;
16797 //        
16798 //        for (var i =0 ; i < cells.length;i++) {
16799 //            ret  = Math.max(cells[i].rows || 0,ret);
16800 //        }
16801 //        return ret;
16802 //        
16803 //    },
16804     
16805     
16806     addItem : function(ev)
16807     {
16808         // look for vertical location slot in
16809         var cells = this.findCells(ev);
16810         
16811 //        ev.row = this.findBestRow(cells);
16812         
16813         // work out the location.
16814         
16815         var crow = false;
16816         var rows = [];
16817         for(var i =0; i < cells.length; i++) {
16818             
16819             cells[i].row = cells[0].row;
16820             
16821             if(i == 0){
16822                 cells[i].row = cells[i].row + 1;
16823             }
16824             
16825             if (!crow) {
16826                 crow = {
16827                     start : cells[i],
16828                     end :  cells[i]
16829                 };
16830                 continue;
16831             }
16832             if (crow.start.getY() == cells[i].getY()) {
16833                 // on same row.
16834                 crow.end = cells[i];
16835                 continue;
16836             }
16837             // different row.
16838             rows.push(crow);
16839             crow = {
16840                 start: cells[i],
16841                 end : cells[i]
16842             };
16843             
16844         }
16845         
16846         rows.push(crow);
16847         ev.els = [];
16848         ev.rows = rows;
16849         ev.cells = cells;
16850         
16851         cells[0].events.push(ev);
16852         
16853         this.calevents.push(ev);
16854     },
16855     
16856     clearEvents: function() {
16857         
16858         if(!this.calevents){
16859             return;
16860         }
16861         
16862         Roo.each(this.cells.elements, function(c){
16863             c.row = 0;
16864             c.events = [];
16865             c.more = [];
16866         });
16867         
16868         Roo.each(this.calevents, function(e) {
16869             Roo.each(e.els, function(el) {
16870                 el.un('mouseenter' ,this.onEventEnter, this);
16871                 el.un('mouseleave' ,this.onEventLeave, this);
16872                 el.remove();
16873             },this);
16874         },this);
16875         
16876         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16877             e.remove();
16878         });
16879         
16880     },
16881     
16882     renderEvents: function()
16883     {   
16884         var _this = this;
16885         
16886         this.cells.each(function(c) {
16887             
16888             if(c.row < 5){
16889                 return;
16890             }
16891             
16892             var ev = c.events;
16893             
16894             var r = 4;
16895             if(c.row != c.events.length){
16896                 r = 4 - (4 - (c.row - c.events.length));
16897             }
16898             
16899             c.events = ev.slice(0, r);
16900             c.more = ev.slice(r);
16901             
16902             if(c.more.length && c.more.length == 1){
16903                 c.events.push(c.more.pop());
16904             }
16905             
16906             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16907             
16908         });
16909             
16910         this.cells.each(function(c) {
16911             
16912             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16913             
16914             
16915             for (var e = 0; e < c.events.length; e++){
16916                 var ev = c.events[e];
16917                 var rows = ev.rows;
16918                 
16919                 for(var i = 0; i < rows.length; i++) {
16920                 
16921                     // how many rows should it span..
16922
16923                     var  cfg = {
16924                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16925                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16926
16927                         unselectable : "on",
16928                         cn : [
16929                             {
16930                                 cls: 'fc-event-inner',
16931                                 cn : [
16932     //                                {
16933     //                                  tag:'span',
16934     //                                  cls: 'fc-event-time',
16935     //                                  html : cells.length > 1 ? '' : ev.time
16936     //                                },
16937                                     {
16938                                       tag:'span',
16939                                       cls: 'fc-event-title',
16940                                       html : String.format('{0}', ev.title)
16941                                     }
16942
16943
16944                                 ]
16945                             },
16946                             {
16947                                 cls: 'ui-resizable-handle ui-resizable-e',
16948                                 html : '&nbsp;&nbsp;&nbsp'
16949                             }
16950
16951                         ]
16952                     };
16953
16954                     if (i == 0) {
16955                         cfg.cls += ' fc-event-start';
16956                     }
16957                     if ((i+1) == rows.length) {
16958                         cfg.cls += ' fc-event-end';
16959                     }
16960
16961                     var ctr = _this.el.select('.fc-event-container',true).first();
16962                     var cg = ctr.createChild(cfg);
16963
16964                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16965                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16966
16967                     var r = (c.more.length) ? 1 : 0;
16968                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16969                     cg.setWidth(ebox.right - sbox.x -2);
16970
16971                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16972                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16973                     cg.on('click', _this.onEventClick, _this, ev);
16974
16975                     ev.els.push(cg);
16976                     
16977                 }
16978                 
16979             }
16980             
16981             
16982             if(c.more.length){
16983                 var  cfg = {
16984                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16985                     style : 'position: absolute',
16986                     unselectable : "on",
16987                     cn : [
16988                         {
16989                             cls: 'fc-event-inner',
16990                             cn : [
16991                                 {
16992                                   tag:'span',
16993                                   cls: 'fc-event-title',
16994                                   html : 'More'
16995                                 }
16996
16997
16998                             ]
16999                         },
17000                         {
17001                             cls: 'ui-resizable-handle ui-resizable-e',
17002                             html : '&nbsp;&nbsp;&nbsp'
17003                         }
17004
17005                     ]
17006                 };
17007
17008                 var ctr = _this.el.select('.fc-event-container',true).first();
17009                 var cg = ctr.createChild(cfg);
17010
17011                 var sbox = c.select('.fc-day-content',true).first().getBox();
17012                 var ebox = c.select('.fc-day-content',true).first().getBox();
17013                 //Roo.log(cg);
17014                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17015                 cg.setWidth(ebox.right - sbox.x -2);
17016
17017                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17018                 
17019             }
17020             
17021         });
17022         
17023         
17024         
17025     },
17026     
17027     onEventEnter: function (e, el,event,d) {
17028         this.fireEvent('evententer', this, el, event);
17029     },
17030     
17031     onEventLeave: function (e, el,event,d) {
17032         this.fireEvent('eventleave', this, el, event);
17033     },
17034     
17035     onEventClick: function (e, el,event,d) {
17036         this.fireEvent('eventclick', this, el, event);
17037     },
17038     
17039     onMonthChange: function () {
17040         this.store.load();
17041     },
17042     
17043     onMoreEventClick: function(e, el, more)
17044     {
17045         var _this = this;
17046         
17047         this.calpopover.placement = 'right';
17048         this.calpopover.setTitle('More');
17049         
17050         this.calpopover.setContent('');
17051         
17052         var ctr = this.calpopover.el.select('.popover-content', true).first();
17053         
17054         Roo.each(more, function(m){
17055             var cfg = {
17056                 cls : 'fc-event-hori fc-event-draggable',
17057                 html : m.title
17058             };
17059             var cg = ctr.createChild(cfg);
17060             
17061             cg.on('click', _this.onEventClick, _this, m);
17062         });
17063         
17064         this.calpopover.show(el);
17065         
17066         
17067     },
17068     
17069     onLoad: function () 
17070     {   
17071         this.calevents = [];
17072         var cal = this;
17073         
17074         if(this.store.getCount() > 0){
17075             this.store.data.each(function(d){
17076                cal.addItem({
17077                     id : d.data.id,
17078                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17079                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17080                     time : d.data.start_time,
17081                     title : d.data.title,
17082                     description : d.data.description,
17083                     venue : d.data.venue
17084                 });
17085             });
17086         }
17087         
17088         this.renderEvents();
17089         
17090         if(this.calevents.length && this.loadMask){
17091             this.maskEl.hide();
17092         }
17093     },
17094     
17095     onBeforeLoad: function()
17096     {
17097         this.clearEvents();
17098         if(this.loadMask){
17099             this.maskEl.show();
17100         }
17101     }
17102 });
17103
17104  
17105  /*
17106  * - LGPL
17107  *
17108  * element
17109  * 
17110  */
17111
17112 /**
17113  * @class Roo.bootstrap.Popover
17114  * @extends Roo.bootstrap.Component
17115  * Bootstrap Popover class
17116  * @cfg {String} html contents of the popover   (or false to use children..)
17117  * @cfg {String} title of popover (or false to hide)
17118  * @cfg {String} placement how it is placed
17119  * @cfg {String} trigger click || hover (or false to trigger manually)
17120  * @cfg {String} over what (parent or false to trigger manually.)
17121  * @cfg {Number} delay - delay before showing
17122  
17123  * @constructor
17124  * Create a new Popover
17125  * @param {Object} config The config object
17126  */
17127
17128 Roo.bootstrap.Popover = function(config){
17129     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17130     
17131     this.addEvents({
17132         // raw events
17133          /**
17134          * @event show
17135          * After the popover show
17136          * 
17137          * @param {Roo.bootstrap.Popover} this
17138          */
17139         "show" : true,
17140         /**
17141          * @event hide
17142          * After the popover hide
17143          * 
17144          * @param {Roo.bootstrap.Popover} this
17145          */
17146         "hide" : true
17147     });
17148 };
17149
17150 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17151     
17152     title: 'Fill in a title',
17153     html: false,
17154     
17155     placement : 'right',
17156     trigger : 'hover', // hover
17157     
17158     delay : 0,
17159     
17160     over: 'parent',
17161     
17162     can_build_overlaid : false,
17163     
17164     getChildContainer : function()
17165     {
17166         return this.el.select('.popover-content',true).first();
17167     },
17168     
17169     getAutoCreate : function(){
17170          
17171         var cfg = {
17172            cls : 'popover roo-dynamic',
17173            style: 'display:block',
17174            cn : [
17175                 {
17176                     cls : 'arrow'
17177                 },
17178                 {
17179                     cls : 'popover-inner',
17180                     cn : [
17181                         {
17182                             tag: 'h3',
17183                             cls: 'popover-title',
17184                             html : this.title
17185                         },
17186                         {
17187                             cls : 'popover-content',
17188                             html : this.html
17189                         }
17190                     ]
17191                     
17192                 }
17193            ]
17194         };
17195         
17196         return cfg;
17197     },
17198     setTitle: function(str)
17199     {
17200         this.title = str;
17201         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17202     },
17203     setContent: function(str)
17204     {
17205         this.html = str;
17206         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17207     },
17208     // as it get's added to the bottom of the page.
17209     onRender : function(ct, position)
17210     {
17211         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17212         if(!this.el){
17213             var cfg = Roo.apply({},  this.getAutoCreate());
17214             cfg.id = Roo.id();
17215             
17216             if (this.cls) {
17217                 cfg.cls += ' ' + this.cls;
17218             }
17219             if (this.style) {
17220                 cfg.style = this.style;
17221             }
17222             //Roo.log("adding to ");
17223             this.el = Roo.get(document.body).createChild(cfg, position);
17224 //            Roo.log(this.el);
17225         }
17226         this.initEvents();
17227     },
17228     
17229     initEvents : function()
17230     {
17231         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17232         this.el.enableDisplayMode('block');
17233         this.el.hide();
17234         if (this.over === false) {
17235             return; 
17236         }
17237         if (this.triggers === false) {
17238             return;
17239         }
17240         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17241         var triggers = this.trigger ? this.trigger.split(' ') : [];
17242         Roo.each(triggers, function(trigger) {
17243         
17244             if (trigger == 'click') {
17245                 on_el.on('click', this.toggle, this);
17246             } else if (trigger != 'manual') {
17247                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17248                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17249       
17250                 on_el.on(eventIn  ,this.enter, this);
17251                 on_el.on(eventOut, this.leave, this);
17252             }
17253         }, this);
17254         
17255     },
17256     
17257     
17258     // private
17259     timeout : null,
17260     hoverState : null,
17261     
17262     toggle : function () {
17263         this.hoverState == 'in' ? this.leave() : this.enter();
17264     },
17265     
17266     enter : function () {
17267         
17268         clearTimeout(this.timeout);
17269     
17270         this.hoverState = 'in';
17271     
17272         if (!this.delay || !this.delay.show) {
17273             this.show();
17274             return;
17275         }
17276         var _t = this;
17277         this.timeout = setTimeout(function () {
17278             if (_t.hoverState == 'in') {
17279                 _t.show();
17280             }
17281         }, this.delay.show)
17282     },
17283     
17284     leave : function() {
17285         clearTimeout(this.timeout);
17286     
17287         this.hoverState = 'out';
17288     
17289         if (!this.delay || !this.delay.hide) {
17290             this.hide();
17291             return;
17292         }
17293         var _t = this;
17294         this.timeout = setTimeout(function () {
17295             if (_t.hoverState == 'out') {
17296                 _t.hide();
17297             }
17298         }, this.delay.hide)
17299     },
17300     
17301     show : function (on_el)
17302     {
17303         if (!on_el) {
17304             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17305         }
17306         
17307         // set content.
17308         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17309         if (this.html !== false) {
17310             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17311         }
17312         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17313         if (!this.title.length) {
17314             this.el.select('.popover-title',true).hide();
17315         }
17316         
17317         var placement = typeof this.placement == 'function' ?
17318             this.placement.call(this, this.el, on_el) :
17319             this.placement;
17320             
17321         var autoToken = /\s?auto?\s?/i;
17322         var autoPlace = autoToken.test(placement);
17323         if (autoPlace) {
17324             placement = placement.replace(autoToken, '') || 'top';
17325         }
17326         
17327         //this.el.detach()
17328         //this.el.setXY([0,0]);
17329         this.el.show();
17330         this.el.dom.style.display='block';
17331         this.el.addClass(placement);
17332         
17333         //this.el.appendTo(on_el);
17334         
17335         var p = this.getPosition();
17336         var box = this.el.getBox();
17337         
17338         if (autoPlace) {
17339             // fixme..
17340         }
17341         var align = Roo.bootstrap.Popover.alignment[placement];
17342         
17343 //        Roo.log(align);
17344         this.el.alignTo(on_el, align[0],align[1]);
17345         //var arrow = this.el.select('.arrow',true).first();
17346         //arrow.set(align[2], 
17347         
17348         this.el.addClass('in');
17349         
17350         
17351         if (this.el.hasClass('fade')) {
17352             // fade it?
17353         }
17354         
17355         this.hoverState = 'in';
17356         
17357         this.fireEvent('show', this);
17358         
17359     },
17360     hide : function()
17361     {
17362         this.el.setXY([0,0]);
17363         this.el.removeClass('in');
17364         this.el.hide();
17365         this.hoverState = null;
17366         
17367         this.fireEvent('hide', this);
17368     }
17369     
17370 });
17371
17372 Roo.bootstrap.Popover.alignment = {
17373     'left' : ['r-l', [-10,0], 'right'],
17374     'right' : ['l-r', [10,0], 'left'],
17375     'bottom' : ['t-b', [0,10], 'top'],
17376     'top' : [ 'b-t', [0,-10], 'bottom']
17377 };
17378
17379  /*
17380  * - LGPL
17381  *
17382  * Progress
17383  * 
17384  */
17385
17386 /**
17387  * @class Roo.bootstrap.Progress
17388  * @extends Roo.bootstrap.Component
17389  * Bootstrap Progress class
17390  * @cfg {Boolean} striped striped of the progress bar
17391  * @cfg {Boolean} active animated of the progress bar
17392  * 
17393  * 
17394  * @constructor
17395  * Create a new Progress
17396  * @param {Object} config The config object
17397  */
17398
17399 Roo.bootstrap.Progress = function(config){
17400     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17401 };
17402
17403 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17404     
17405     striped : false,
17406     active: false,
17407     
17408     getAutoCreate : function(){
17409         var cfg = {
17410             tag: 'div',
17411             cls: 'progress'
17412         };
17413         
17414         
17415         if(this.striped){
17416             cfg.cls += ' progress-striped';
17417         }
17418       
17419         if(this.active){
17420             cfg.cls += ' active';
17421         }
17422         
17423         
17424         return cfg;
17425     }
17426    
17427 });
17428
17429  
17430
17431  /*
17432  * - LGPL
17433  *
17434  * ProgressBar
17435  * 
17436  */
17437
17438 /**
17439  * @class Roo.bootstrap.ProgressBar
17440  * @extends Roo.bootstrap.Component
17441  * Bootstrap ProgressBar class
17442  * @cfg {Number} aria_valuenow aria-value now
17443  * @cfg {Number} aria_valuemin aria-value min
17444  * @cfg {Number} aria_valuemax aria-value max
17445  * @cfg {String} label label for the progress bar
17446  * @cfg {String} panel (success | info | warning | danger )
17447  * @cfg {String} role role of the progress bar
17448  * @cfg {String} sr_only text
17449  * 
17450  * 
17451  * @constructor
17452  * Create a new ProgressBar
17453  * @param {Object} config The config object
17454  */
17455
17456 Roo.bootstrap.ProgressBar = function(config){
17457     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17458 };
17459
17460 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17461     
17462     aria_valuenow : 0,
17463     aria_valuemin : 0,
17464     aria_valuemax : 100,
17465     label : false,
17466     panel : false,
17467     role : false,
17468     sr_only: false,
17469     
17470     getAutoCreate : function()
17471     {
17472         
17473         var cfg = {
17474             tag: 'div',
17475             cls: 'progress-bar',
17476             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17477         };
17478         
17479         if(this.sr_only){
17480             cfg.cn = {
17481                 tag: 'span',
17482                 cls: 'sr-only',
17483                 html: this.sr_only
17484             }
17485         }
17486         
17487         if(this.role){
17488             cfg.role = this.role;
17489         }
17490         
17491         if(this.aria_valuenow){
17492             cfg['aria-valuenow'] = this.aria_valuenow;
17493         }
17494         
17495         if(this.aria_valuemin){
17496             cfg['aria-valuemin'] = this.aria_valuemin;
17497         }
17498         
17499         if(this.aria_valuemax){
17500             cfg['aria-valuemax'] = this.aria_valuemax;
17501         }
17502         
17503         if(this.label && !this.sr_only){
17504             cfg.html = this.label;
17505         }
17506         
17507         if(this.panel){
17508             cfg.cls += ' progress-bar-' + this.panel;
17509         }
17510         
17511         return cfg;
17512     },
17513     
17514     update : function(aria_valuenow)
17515     {
17516         this.aria_valuenow = aria_valuenow;
17517         
17518         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17519     }
17520    
17521 });
17522
17523  
17524
17525  /*
17526  * - LGPL
17527  *
17528  * column
17529  * 
17530  */
17531
17532 /**
17533  * @class Roo.bootstrap.TabGroup
17534  * @extends Roo.bootstrap.Column
17535  * Bootstrap Column class
17536  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17537  * @cfg {Boolean} carousel true to make the group behave like a carousel
17538  * @cfg {Boolean} bullets show bullets for the panels
17539  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17540  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17541  * @cfg {Boolean} showarrow (true|false) show arrow default true
17542  * 
17543  * @constructor
17544  * Create a new TabGroup
17545  * @param {Object} config The config object
17546  */
17547
17548 Roo.bootstrap.TabGroup = function(config){
17549     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17550     if (!this.navId) {
17551         this.navId = Roo.id();
17552     }
17553     this.tabs = [];
17554     Roo.bootstrap.TabGroup.register(this);
17555     
17556 };
17557
17558 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17559     
17560     carousel : false,
17561     transition : false,
17562     bullets : 0,
17563     timer : 0,
17564     autoslide : false,
17565     slideFn : false,
17566     slideOnTouch : false,
17567     showarrow : true,
17568     
17569     getAutoCreate : function()
17570     {
17571         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17572         
17573         cfg.cls += ' tab-content';
17574         
17575         if (this.carousel) {
17576             cfg.cls += ' carousel slide';
17577             
17578             cfg.cn = [{
17579                cls : 'carousel-inner',
17580                cn : []
17581             }];
17582         
17583             if(this.bullets  && !Roo.isTouch){
17584                 
17585                 var bullets = {
17586                     cls : 'carousel-bullets',
17587                     cn : []
17588                 };
17589                
17590                 if(this.bullets_cls){
17591                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17592                 }
17593                 
17594                 bullets.cn.push({
17595                     cls : 'clear'
17596                 });
17597                 
17598                 cfg.cn[0].cn.push(bullets);
17599             }
17600             
17601             if(this.showarrow){
17602                 cfg.cn[0].cn.push({
17603                     tag : 'div',
17604                     class : 'carousel-arrow',
17605                     cn : [
17606                         {
17607                             tag : 'div',
17608                             class : 'carousel-prev',
17609                             cn : [
17610                                 {
17611                                     tag : 'i',
17612                                     class : 'fa fa-chevron-left'
17613                                 }
17614                             ]
17615                         },
17616                         {
17617                             tag : 'div',
17618                             class : 'carousel-next',
17619                             cn : [
17620                                 {
17621                                     tag : 'i',
17622                                     class : 'fa fa-chevron-right'
17623                                 }
17624                             ]
17625                         }
17626                     ]
17627                 });
17628             }
17629             
17630         }
17631         
17632         return cfg;
17633     },
17634     
17635     initEvents:  function()
17636     {
17637 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17638 //            this.el.on("touchstart", this.onTouchStart, this);
17639 //        }
17640         
17641         if(this.autoslide){
17642             var _this = this;
17643             
17644             this.slideFn = window.setInterval(function() {
17645                 _this.showPanelNext();
17646             }, this.timer);
17647         }
17648         
17649         if(this.showarrow){
17650             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17651             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17652         }
17653         
17654         
17655     },
17656     
17657 //    onTouchStart : function(e, el, o)
17658 //    {
17659 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17660 //            return;
17661 //        }
17662 //        
17663 //        this.showPanelNext();
17664 //    },
17665     
17666     
17667     getChildContainer : function()
17668     {
17669         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17670     },
17671     
17672     /**
17673     * register a Navigation item
17674     * @param {Roo.bootstrap.NavItem} the navitem to add
17675     */
17676     register : function(item)
17677     {
17678         this.tabs.push( item);
17679         item.navId = this.navId; // not really needed..
17680         this.addBullet();
17681     
17682     },
17683     
17684     getActivePanel : function()
17685     {
17686         var r = false;
17687         Roo.each(this.tabs, function(t) {
17688             if (t.active) {
17689                 r = t;
17690                 return false;
17691             }
17692             return null;
17693         });
17694         return r;
17695         
17696     },
17697     getPanelByName : function(n)
17698     {
17699         var r = false;
17700         Roo.each(this.tabs, function(t) {
17701             if (t.tabId == n) {
17702                 r = t;
17703                 return false;
17704             }
17705             return null;
17706         });
17707         return r;
17708     },
17709     indexOfPanel : function(p)
17710     {
17711         var r = false;
17712         Roo.each(this.tabs, function(t,i) {
17713             if (t.tabId == p.tabId) {
17714                 r = i;
17715                 return false;
17716             }
17717             return null;
17718         });
17719         return r;
17720     },
17721     /**
17722      * show a specific panel
17723      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17724      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17725      */
17726     showPanel : function (pan)
17727     {
17728         if(this.transition || typeof(pan) == 'undefined'){
17729             Roo.log("waiting for the transitionend");
17730             return;
17731         }
17732         
17733         if (typeof(pan) == 'number') {
17734             pan = this.tabs[pan];
17735         }
17736         
17737         if (typeof(pan) == 'string') {
17738             pan = this.getPanelByName(pan);
17739         }
17740         
17741         var cur = this.getActivePanel();
17742         
17743         if(!pan || !cur){
17744             Roo.log('pan or acitve pan is undefined');
17745             return false;
17746         }
17747         
17748         if (pan.tabId == this.getActivePanel().tabId) {
17749             return true;
17750         }
17751         
17752         if (false === cur.fireEvent('beforedeactivate')) {
17753             return false;
17754         }
17755         
17756         if(this.bullets > 0 && !Roo.isTouch){
17757             this.setActiveBullet(this.indexOfPanel(pan));
17758         }
17759         
17760         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17761             
17762             this.transition = true;
17763             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17764             var lr = dir == 'next' ? 'left' : 'right';
17765             pan.el.addClass(dir); // or prev
17766             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17767             cur.el.addClass(lr); // or right
17768             pan.el.addClass(lr);
17769             
17770             var _this = this;
17771             cur.el.on('transitionend', function() {
17772                 Roo.log("trans end?");
17773                 
17774                 pan.el.removeClass([lr,dir]);
17775                 pan.setActive(true);
17776                 
17777                 cur.el.removeClass([lr]);
17778                 cur.setActive(false);
17779                 
17780                 _this.transition = false;
17781                 
17782             }, this, { single:  true } );
17783             
17784             return true;
17785         }
17786         
17787         cur.setActive(false);
17788         pan.setActive(true);
17789         
17790         return true;
17791         
17792     },
17793     showPanelNext : function()
17794     {
17795         var i = this.indexOfPanel(this.getActivePanel());
17796         
17797         if (i >= this.tabs.length - 1 && !this.autoslide) {
17798             return;
17799         }
17800         
17801         if (i >= this.tabs.length - 1 && this.autoslide) {
17802             i = -1;
17803         }
17804         
17805         this.showPanel(this.tabs[i+1]);
17806     },
17807     
17808     showPanelPrev : function()
17809     {
17810         var i = this.indexOfPanel(this.getActivePanel());
17811         
17812         if (i  < 1 && !this.autoslide) {
17813             return;
17814         }
17815         
17816         if (i < 1 && this.autoslide) {
17817             i = this.tabs.length;
17818         }
17819         
17820         this.showPanel(this.tabs[i-1]);
17821     },
17822     
17823     
17824     addBullet: function()
17825     {
17826         if(!this.bullets || Roo.isTouch){
17827             return;
17828         }
17829         var ctr = this.el.select('.carousel-bullets',true).first();
17830         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17831         var bullet = ctr.createChild({
17832             cls : 'bullet bullet-' + i
17833         },ctr.dom.lastChild);
17834         
17835         
17836         var _this = this;
17837         
17838         bullet.on('click', (function(e, el, o, ii, t){
17839
17840             e.preventDefault();
17841
17842             this.showPanel(ii);
17843
17844             if(this.autoslide && this.slideFn){
17845                 clearInterval(this.slideFn);
17846                 this.slideFn = window.setInterval(function() {
17847                     _this.showPanelNext();
17848                 }, this.timer);
17849             }
17850
17851         }).createDelegate(this, [i, bullet], true));
17852                 
17853         
17854     },
17855      
17856     setActiveBullet : function(i)
17857     {
17858         if(Roo.isTouch){
17859             return;
17860         }
17861         
17862         Roo.each(this.el.select('.bullet', true).elements, function(el){
17863             el.removeClass('selected');
17864         });
17865
17866         var bullet = this.el.select('.bullet-' + i, true).first();
17867         
17868         if(!bullet){
17869             return;
17870         }
17871         
17872         bullet.addClass('selected');
17873     }
17874     
17875     
17876   
17877 });
17878
17879  
17880
17881  
17882  
17883 Roo.apply(Roo.bootstrap.TabGroup, {
17884     
17885     groups: {},
17886      /**
17887     * register a Navigation Group
17888     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17889     */
17890     register : function(navgrp)
17891     {
17892         this.groups[navgrp.navId] = navgrp;
17893         
17894     },
17895     /**
17896     * fetch a Navigation Group based on the navigation ID
17897     * if one does not exist , it will get created.
17898     * @param {string} the navgroup to add
17899     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17900     */
17901     get: function(navId) {
17902         if (typeof(this.groups[navId]) == 'undefined') {
17903             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17904         }
17905         return this.groups[navId] ;
17906     }
17907     
17908     
17909     
17910 });
17911
17912  /*
17913  * - LGPL
17914  *
17915  * TabPanel
17916  * 
17917  */
17918
17919 /**
17920  * @class Roo.bootstrap.TabPanel
17921  * @extends Roo.bootstrap.Component
17922  * Bootstrap TabPanel class
17923  * @cfg {Boolean} active panel active
17924  * @cfg {String} html panel content
17925  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17926  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17927  * @cfg {String} href click to link..
17928  * 
17929  * 
17930  * @constructor
17931  * Create a new TabPanel
17932  * @param {Object} config The config object
17933  */
17934
17935 Roo.bootstrap.TabPanel = function(config){
17936     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17937     this.addEvents({
17938         /**
17939              * @event changed
17940              * Fires when the active status changes
17941              * @param {Roo.bootstrap.TabPanel} this
17942              * @param {Boolean} state the new state
17943             
17944          */
17945         'changed': true,
17946         /**
17947              * @event beforedeactivate
17948              * Fires before a tab is de-activated - can be used to do validation on a form.
17949              * @param {Roo.bootstrap.TabPanel} this
17950              * @return {Boolean} false if there is an error
17951             
17952          */
17953         'beforedeactivate': true
17954      });
17955     
17956     this.tabId = this.tabId || Roo.id();
17957   
17958 };
17959
17960 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17961     
17962     active: false,
17963     html: false,
17964     tabId: false,
17965     navId : false,
17966     href : '',
17967     
17968     getAutoCreate : function(){
17969         var cfg = {
17970             tag: 'div',
17971             // item is needed for carousel - not sure if it has any effect otherwise
17972             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17973             html: this.html || ''
17974         };
17975         
17976         if(this.active){
17977             cfg.cls += ' active';
17978         }
17979         
17980         if(this.tabId){
17981             cfg.tabId = this.tabId;
17982         }
17983         
17984         
17985         return cfg;
17986     },
17987     
17988     initEvents:  function()
17989     {
17990         var p = this.parent();
17991         
17992         this.navId = this.navId || p.navId;
17993         
17994         if (typeof(this.navId) != 'undefined') {
17995             // not really needed.. but just in case.. parent should be a NavGroup.
17996             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17997             
17998             tg.register(this);
17999             
18000             var i = tg.tabs.length - 1;
18001             
18002             if(this.active && tg.bullets > 0 && i < tg.bullets){
18003                 tg.setActiveBullet(i);
18004             }
18005         }
18006         
18007         this.el.on('click', this.onClick, this);
18008         
18009         if(Roo.isTouch){
18010             this.el.on("touchstart", this.onTouchStart, this);
18011             this.el.on("touchmove", this.onTouchMove, this);
18012             this.el.on("touchend", this.onTouchEnd, this);
18013         }
18014         
18015     },
18016     
18017     onRender : function(ct, position)
18018     {
18019         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18020     },
18021     
18022     setActive : function(state)
18023     {
18024         Roo.log("panel - set active " + this.tabId + "=" + state);
18025         
18026         this.active = state;
18027         if (!state) {
18028             this.el.removeClass('active');
18029             
18030         } else  if (!this.el.hasClass('active')) {
18031             this.el.addClass('active');
18032         }
18033         
18034         this.fireEvent('changed', this, state);
18035     },
18036     
18037     onClick : function(e)
18038     {
18039         e.preventDefault();
18040         
18041         if(!this.href.length){
18042             return;
18043         }
18044         
18045         window.location.href = this.href;
18046     },
18047     
18048     startX : 0,
18049     startY : 0,
18050     endX : 0,
18051     endY : 0,
18052     swiping : false,
18053     
18054     onTouchStart : function(e)
18055     {
18056         this.swiping = false;
18057         
18058         this.startX = e.browserEvent.touches[0].clientX;
18059         this.startY = e.browserEvent.touches[0].clientY;
18060     },
18061     
18062     onTouchMove : function(e)
18063     {
18064         this.swiping = true;
18065         
18066         this.endX = e.browserEvent.touches[0].clientX;
18067         this.endY = e.browserEvent.touches[0].clientY;
18068     },
18069     
18070     onTouchEnd : function(e)
18071     {
18072         if(!this.swiping){
18073             this.onClick(e);
18074             return;
18075         }
18076         
18077         var tabGroup = this.parent();
18078         
18079         if(this.endX > this.startX){ // swiping right
18080             tabGroup.showPanelPrev();
18081             return;
18082         }
18083         
18084         if(this.startX > this.endX){ // swiping left
18085             tabGroup.showPanelNext();
18086             return;
18087         }
18088     }
18089     
18090     
18091 });
18092  
18093
18094  
18095
18096  /*
18097  * - LGPL
18098  *
18099  * DateField
18100  * 
18101  */
18102
18103 /**
18104  * @class Roo.bootstrap.DateField
18105  * @extends Roo.bootstrap.Input
18106  * Bootstrap DateField class
18107  * @cfg {Number} weekStart default 0
18108  * @cfg {String} viewMode default empty, (months|years)
18109  * @cfg {String} minViewMode default empty, (months|years)
18110  * @cfg {Number} startDate default -Infinity
18111  * @cfg {Number} endDate default Infinity
18112  * @cfg {Boolean} todayHighlight default false
18113  * @cfg {Boolean} todayBtn default false
18114  * @cfg {Boolean} calendarWeeks default false
18115  * @cfg {Object} daysOfWeekDisabled default empty
18116  * @cfg {Boolean} singleMode default false (true | false)
18117  * 
18118  * @cfg {Boolean} keyboardNavigation default true
18119  * @cfg {String} language default en
18120  * 
18121  * @constructor
18122  * Create a new DateField
18123  * @param {Object} config The config object
18124  */
18125
18126 Roo.bootstrap.DateField = function(config){
18127     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18128      this.addEvents({
18129             /**
18130              * @event show
18131              * Fires when this field show.
18132              * @param {Roo.bootstrap.DateField} this
18133              * @param {Mixed} date The date value
18134              */
18135             show : true,
18136             /**
18137              * @event show
18138              * Fires when this field hide.
18139              * @param {Roo.bootstrap.DateField} this
18140              * @param {Mixed} date The date value
18141              */
18142             hide : true,
18143             /**
18144              * @event select
18145              * Fires when select a date.
18146              * @param {Roo.bootstrap.DateField} this
18147              * @param {Mixed} date The date value
18148              */
18149             select : true,
18150             /**
18151              * @event beforeselect
18152              * Fires when before select a date.
18153              * @param {Roo.bootstrap.DateField} this
18154              * @param {Mixed} date The date value
18155              */
18156             beforeselect : true
18157         });
18158 };
18159
18160 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18161     
18162     /**
18163      * @cfg {String} format
18164      * The default date format string which can be overriden for localization support.  The format must be
18165      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18166      */
18167     format : "m/d/y",
18168     /**
18169      * @cfg {String} altFormats
18170      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18171      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18172      */
18173     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18174     
18175     weekStart : 0,
18176     
18177     viewMode : '',
18178     
18179     minViewMode : '',
18180     
18181     todayHighlight : false,
18182     
18183     todayBtn: false,
18184     
18185     language: 'en',
18186     
18187     keyboardNavigation: true,
18188     
18189     calendarWeeks: false,
18190     
18191     startDate: -Infinity,
18192     
18193     endDate: Infinity,
18194     
18195     daysOfWeekDisabled: [],
18196     
18197     _events: [],
18198     
18199     singleMode : false,
18200     
18201     UTCDate: function()
18202     {
18203         return new Date(Date.UTC.apply(Date, arguments));
18204     },
18205     
18206     UTCToday: function()
18207     {
18208         var today = new Date();
18209         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18210     },
18211     
18212     getDate: function() {
18213             var d = this.getUTCDate();
18214             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18215     },
18216     
18217     getUTCDate: function() {
18218             return this.date;
18219     },
18220     
18221     setDate: function(d) {
18222             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18223     },
18224     
18225     setUTCDate: function(d) {
18226             this.date = d;
18227             this.setValue(this.formatDate(this.date));
18228     },
18229         
18230     onRender: function(ct, position)
18231     {
18232         
18233         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18234         
18235         this.language = this.language || 'en';
18236         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18237         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18238         
18239         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18240         this.format = this.format || 'm/d/y';
18241         this.isInline = false;
18242         this.isInput = true;
18243         this.component = this.el.select('.add-on', true).first() || false;
18244         this.component = (this.component && this.component.length === 0) ? false : this.component;
18245         this.hasInput = this.component && this.inputEl().length;
18246         
18247         if (typeof(this.minViewMode === 'string')) {
18248             switch (this.minViewMode) {
18249                 case 'months':
18250                     this.minViewMode = 1;
18251                     break;
18252                 case 'years':
18253                     this.minViewMode = 2;
18254                     break;
18255                 default:
18256                     this.minViewMode = 0;
18257                     break;
18258             }
18259         }
18260         
18261         if (typeof(this.viewMode === 'string')) {
18262             switch (this.viewMode) {
18263                 case 'months':
18264                     this.viewMode = 1;
18265                     break;
18266                 case 'years':
18267                     this.viewMode = 2;
18268                     break;
18269                 default:
18270                     this.viewMode = 0;
18271                     break;
18272             }
18273         }
18274                 
18275         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18276         
18277 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18278         
18279         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18280         
18281         this.picker().on('mousedown', this.onMousedown, this);
18282         this.picker().on('click', this.onClick, this);
18283         
18284         this.picker().addClass('datepicker-dropdown');
18285         
18286         this.startViewMode = this.viewMode;
18287         
18288         if(this.singleMode){
18289             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18290                 v.setVisibilityMode(Roo.Element.DISPLAY);
18291                 v.hide();
18292             });
18293             
18294             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18295                 v.setStyle('width', '189px');
18296             });
18297         }
18298         
18299         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18300             if(!this.calendarWeeks){
18301                 v.remove();
18302                 return;
18303             }
18304             
18305             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18306             v.attr('colspan', function(i, val){
18307                 return parseInt(val) + 1;
18308             });
18309         });
18310                         
18311         
18312         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18313         
18314         this.setStartDate(this.startDate);
18315         this.setEndDate(this.endDate);
18316         
18317         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18318         
18319         this.fillDow();
18320         this.fillMonths();
18321         this.update();
18322         this.showMode();
18323         
18324         if(this.isInline) {
18325             this.show();
18326         }
18327     },
18328     
18329     picker : function()
18330     {
18331         return this.pickerEl;
18332 //        return this.el.select('.datepicker', true).first();
18333     },
18334     
18335     fillDow: function()
18336     {
18337         var dowCnt = this.weekStart;
18338         
18339         var dow = {
18340             tag: 'tr',
18341             cn: [
18342                 
18343             ]
18344         };
18345         
18346         if(this.calendarWeeks){
18347             dow.cn.push({
18348                 tag: 'th',
18349                 cls: 'cw',
18350                 html: '&nbsp;'
18351             })
18352         }
18353         
18354         while (dowCnt < this.weekStart + 7) {
18355             dow.cn.push({
18356                 tag: 'th',
18357                 cls: 'dow',
18358                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18359             });
18360         }
18361         
18362         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18363     },
18364     
18365     fillMonths: function()
18366     {    
18367         var i = 0;
18368         var months = this.picker().select('>.datepicker-months td', true).first();
18369         
18370         months.dom.innerHTML = '';
18371         
18372         while (i < 12) {
18373             var month = {
18374                 tag: 'span',
18375                 cls: 'month',
18376                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18377             };
18378             
18379             months.createChild(month);
18380         }
18381         
18382     },
18383     
18384     update: function()
18385     {
18386         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;
18387         
18388         if (this.date < this.startDate) {
18389             this.viewDate = new Date(this.startDate);
18390         } else if (this.date > this.endDate) {
18391             this.viewDate = new Date(this.endDate);
18392         } else {
18393             this.viewDate = new Date(this.date);
18394         }
18395         
18396         this.fill();
18397     },
18398     
18399     fill: function() 
18400     {
18401         var d = new Date(this.viewDate),
18402                 year = d.getUTCFullYear(),
18403                 month = d.getUTCMonth(),
18404                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18405                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18406                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18407                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18408                 currentDate = this.date && this.date.valueOf(),
18409                 today = this.UTCToday();
18410         
18411         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18412         
18413 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18414         
18415 //        this.picker.select('>tfoot th.today').
18416 //                                              .text(dates[this.language].today)
18417 //                                              .toggle(this.todayBtn !== false);
18418     
18419         this.updateNavArrows();
18420         this.fillMonths();
18421                                                 
18422         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18423         
18424         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18425          
18426         prevMonth.setUTCDate(day);
18427         
18428         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18429         
18430         var nextMonth = new Date(prevMonth);
18431         
18432         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18433         
18434         nextMonth = nextMonth.valueOf();
18435         
18436         var fillMonths = false;
18437         
18438         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18439         
18440         while(prevMonth.valueOf() < nextMonth) {
18441             var clsName = '';
18442             
18443             if (prevMonth.getUTCDay() === this.weekStart) {
18444                 if(fillMonths){
18445                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18446                 }
18447                     
18448                 fillMonths = {
18449                     tag: 'tr',
18450                     cn: []
18451                 };
18452                 
18453                 if(this.calendarWeeks){
18454                     // ISO 8601: First week contains first thursday.
18455                     // ISO also states week starts on Monday, but we can be more abstract here.
18456                     var
18457                     // Start of current week: based on weekstart/current date
18458                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18459                     // Thursday of this week
18460                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18461                     // First Thursday of year, year from thursday
18462                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18463                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18464                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18465                     
18466                     fillMonths.cn.push({
18467                         tag: 'td',
18468                         cls: 'cw',
18469                         html: calWeek
18470                     });
18471                 }
18472             }
18473             
18474             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18475                 clsName += ' old';
18476             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18477                 clsName += ' new';
18478             }
18479             if (this.todayHighlight &&
18480                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18481                 prevMonth.getUTCMonth() == today.getMonth() &&
18482                 prevMonth.getUTCDate() == today.getDate()) {
18483                 clsName += ' today';
18484             }
18485             
18486             if (currentDate && prevMonth.valueOf() === currentDate) {
18487                 clsName += ' active';
18488             }
18489             
18490             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18491                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18492                     clsName += ' disabled';
18493             }
18494             
18495             fillMonths.cn.push({
18496                 tag: 'td',
18497                 cls: 'day ' + clsName,
18498                 html: prevMonth.getDate()
18499             });
18500             
18501             prevMonth.setDate(prevMonth.getDate()+1);
18502         }
18503           
18504         var currentYear = this.date && this.date.getUTCFullYear();
18505         var currentMonth = this.date && this.date.getUTCMonth();
18506         
18507         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18508         
18509         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18510             v.removeClass('active');
18511             
18512             if(currentYear === year && k === currentMonth){
18513                 v.addClass('active');
18514             }
18515             
18516             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18517                 v.addClass('disabled');
18518             }
18519             
18520         });
18521         
18522         
18523         year = parseInt(year/10, 10) * 10;
18524         
18525         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18526         
18527         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18528         
18529         year -= 1;
18530         for (var i = -1; i < 11; i++) {
18531             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18532                 tag: 'span',
18533                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18534                 html: year
18535             });
18536             
18537             year += 1;
18538         }
18539     },
18540     
18541     showMode: function(dir) 
18542     {
18543         if (dir) {
18544             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18545         }
18546         
18547         Roo.each(this.picker().select('>div',true).elements, function(v){
18548             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18549             v.hide();
18550         });
18551         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18552     },
18553     
18554     place: function()
18555     {
18556         if(this.isInline) {
18557             return;
18558         }
18559         
18560         this.picker().removeClass(['bottom', 'top']);
18561         
18562         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18563             /*
18564              * place to the top of element!
18565              *
18566              */
18567             
18568             this.picker().addClass('top');
18569             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18570             
18571             return;
18572         }
18573         
18574         this.picker().addClass('bottom');
18575         
18576         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18577     },
18578     
18579     parseDate : function(value)
18580     {
18581         if(!value || value instanceof Date){
18582             return value;
18583         }
18584         var v = Date.parseDate(value, this.format);
18585         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18586             v = Date.parseDate(value, 'Y-m-d');
18587         }
18588         if(!v && this.altFormats){
18589             if(!this.altFormatsArray){
18590                 this.altFormatsArray = this.altFormats.split("|");
18591             }
18592             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18593                 v = Date.parseDate(value, this.altFormatsArray[i]);
18594             }
18595         }
18596         return v;
18597     },
18598     
18599     formatDate : function(date, fmt)
18600     {   
18601         return (!date || !(date instanceof Date)) ?
18602         date : date.dateFormat(fmt || this.format);
18603     },
18604     
18605     onFocus : function()
18606     {
18607         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18608         this.show();
18609     },
18610     
18611     onBlur : function()
18612     {
18613         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18614         
18615         var d = this.inputEl().getValue();
18616         
18617         this.setValue(d);
18618                 
18619         this.hide();
18620     },
18621     
18622     show : function()
18623     {
18624         this.picker().show();
18625         this.update();
18626         this.place();
18627         
18628         this.fireEvent('show', this, this.date);
18629     },
18630     
18631     hide : function()
18632     {
18633         if(this.isInline) {
18634             return;
18635         }
18636         this.picker().hide();
18637         this.viewMode = this.startViewMode;
18638         this.showMode();
18639         
18640         this.fireEvent('hide', this, this.date);
18641         
18642     },
18643     
18644     onMousedown: function(e)
18645     {
18646         e.stopPropagation();
18647         e.preventDefault();
18648     },
18649     
18650     keyup: function(e)
18651     {
18652         Roo.bootstrap.DateField.superclass.keyup.call(this);
18653         this.update();
18654     },
18655
18656     setValue: function(v)
18657     {
18658         if(this.fireEvent('beforeselect', this, v) !== false){
18659             var d = new Date(this.parseDate(v) ).clearTime();
18660         
18661             if(isNaN(d.getTime())){
18662                 this.date = this.viewDate = '';
18663                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18664                 return;
18665             }
18666
18667             v = this.formatDate(d);
18668
18669             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18670
18671             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18672
18673             this.update();
18674
18675             this.fireEvent('select', this, this.date);
18676         }
18677     },
18678     
18679     getValue: function()
18680     {
18681         return this.formatDate(this.date);
18682     },
18683     
18684     fireKey: function(e)
18685     {
18686         if (!this.picker().isVisible()){
18687             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18688                 this.show();
18689             }
18690             return;
18691         }
18692         
18693         var dateChanged = false,
18694         dir, day, month,
18695         newDate, newViewDate;
18696         
18697         switch(e.keyCode){
18698             case 27: // escape
18699                 this.hide();
18700                 e.preventDefault();
18701                 break;
18702             case 37: // left
18703             case 39: // right
18704                 if (!this.keyboardNavigation) {
18705                     break;
18706                 }
18707                 dir = e.keyCode == 37 ? -1 : 1;
18708                 
18709                 if (e.ctrlKey){
18710                     newDate = this.moveYear(this.date, dir);
18711                     newViewDate = this.moveYear(this.viewDate, dir);
18712                 } else if (e.shiftKey){
18713                     newDate = this.moveMonth(this.date, dir);
18714                     newViewDate = this.moveMonth(this.viewDate, dir);
18715                 } else {
18716                     newDate = new Date(this.date);
18717                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18718                     newViewDate = new Date(this.viewDate);
18719                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18720                 }
18721                 if (this.dateWithinRange(newDate)){
18722                     this.date = newDate;
18723                     this.viewDate = newViewDate;
18724                     this.setValue(this.formatDate(this.date));
18725 //                    this.update();
18726                     e.preventDefault();
18727                     dateChanged = true;
18728                 }
18729                 break;
18730             case 38: // up
18731             case 40: // down
18732                 if (!this.keyboardNavigation) {
18733                     break;
18734                 }
18735                 dir = e.keyCode == 38 ? -1 : 1;
18736                 if (e.ctrlKey){
18737                     newDate = this.moveYear(this.date, dir);
18738                     newViewDate = this.moveYear(this.viewDate, dir);
18739                 } else if (e.shiftKey){
18740                     newDate = this.moveMonth(this.date, dir);
18741                     newViewDate = this.moveMonth(this.viewDate, dir);
18742                 } else {
18743                     newDate = new Date(this.date);
18744                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18745                     newViewDate = new Date(this.viewDate);
18746                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18747                 }
18748                 if (this.dateWithinRange(newDate)){
18749                     this.date = newDate;
18750                     this.viewDate = newViewDate;
18751                     this.setValue(this.formatDate(this.date));
18752 //                    this.update();
18753                     e.preventDefault();
18754                     dateChanged = true;
18755                 }
18756                 break;
18757             case 13: // enter
18758                 this.setValue(this.formatDate(this.date));
18759                 this.hide();
18760                 e.preventDefault();
18761                 break;
18762             case 9: // tab
18763                 this.setValue(this.formatDate(this.date));
18764                 this.hide();
18765                 break;
18766             case 16: // shift
18767             case 17: // ctrl
18768             case 18: // alt
18769                 break;
18770             default :
18771                 this.hide();
18772                 
18773         }
18774     },
18775     
18776     
18777     onClick: function(e) 
18778     {
18779         e.stopPropagation();
18780         e.preventDefault();
18781         
18782         var target = e.getTarget();
18783         
18784         if(target.nodeName.toLowerCase() === 'i'){
18785             target = Roo.get(target).dom.parentNode;
18786         }
18787         
18788         var nodeName = target.nodeName;
18789         var className = target.className;
18790         var html = target.innerHTML;
18791         //Roo.log(nodeName);
18792         
18793         switch(nodeName.toLowerCase()) {
18794             case 'th':
18795                 switch(className) {
18796                     case 'switch':
18797                         this.showMode(1);
18798                         break;
18799                     case 'prev':
18800                     case 'next':
18801                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18802                         switch(this.viewMode){
18803                                 case 0:
18804                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18805                                         break;
18806                                 case 1:
18807                                 case 2:
18808                                         this.viewDate = this.moveYear(this.viewDate, dir);
18809                                         break;
18810                         }
18811                         this.fill();
18812                         break;
18813                     case 'today':
18814                         var date = new Date();
18815                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18816 //                        this.fill()
18817                         this.setValue(this.formatDate(this.date));
18818                         
18819                         this.hide();
18820                         break;
18821                 }
18822                 break;
18823             case 'span':
18824                 if (className.indexOf('disabled') < 0) {
18825                     this.viewDate.setUTCDate(1);
18826                     if (className.indexOf('month') > -1) {
18827                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18828                     } else {
18829                         var year = parseInt(html, 10) || 0;
18830                         this.viewDate.setUTCFullYear(year);
18831                         
18832                     }
18833                     
18834                     if(this.singleMode){
18835                         this.setValue(this.formatDate(this.viewDate));
18836                         this.hide();
18837                         return;
18838                     }
18839                     
18840                     this.showMode(-1);
18841                     this.fill();
18842                 }
18843                 break;
18844                 
18845             case 'td':
18846                 //Roo.log(className);
18847                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18848                     var day = parseInt(html, 10) || 1;
18849                     var year = this.viewDate.getUTCFullYear(),
18850                         month = this.viewDate.getUTCMonth();
18851
18852                     if (className.indexOf('old') > -1) {
18853                         if(month === 0 ){
18854                             month = 11;
18855                             year -= 1;
18856                         }else{
18857                             month -= 1;
18858                         }
18859                     } else if (className.indexOf('new') > -1) {
18860                         if (month == 11) {
18861                             month = 0;
18862                             year += 1;
18863                         } else {
18864                             month += 1;
18865                         }
18866                     }
18867                     //Roo.log([year,month,day]);
18868                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18869                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18870 //                    this.fill();
18871                     //Roo.log(this.formatDate(this.date));
18872                     this.setValue(this.formatDate(this.date));
18873                     this.hide();
18874                 }
18875                 break;
18876         }
18877     },
18878     
18879     setStartDate: function(startDate)
18880     {
18881         this.startDate = startDate || -Infinity;
18882         if (this.startDate !== -Infinity) {
18883             this.startDate = this.parseDate(this.startDate);
18884         }
18885         this.update();
18886         this.updateNavArrows();
18887     },
18888
18889     setEndDate: function(endDate)
18890     {
18891         this.endDate = endDate || Infinity;
18892         if (this.endDate !== Infinity) {
18893             this.endDate = this.parseDate(this.endDate);
18894         }
18895         this.update();
18896         this.updateNavArrows();
18897     },
18898     
18899     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18900     {
18901         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18902         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18903             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18904         }
18905         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18906             return parseInt(d, 10);
18907         });
18908         this.update();
18909         this.updateNavArrows();
18910     },
18911     
18912     updateNavArrows: function() 
18913     {
18914         if(this.singleMode){
18915             return;
18916         }
18917         
18918         var d = new Date(this.viewDate),
18919         year = d.getUTCFullYear(),
18920         month = d.getUTCMonth();
18921         
18922         Roo.each(this.picker().select('.prev', true).elements, function(v){
18923             v.show();
18924             switch (this.viewMode) {
18925                 case 0:
18926
18927                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18928                         v.hide();
18929                     }
18930                     break;
18931                 case 1:
18932                 case 2:
18933                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18934                         v.hide();
18935                     }
18936                     break;
18937             }
18938         });
18939         
18940         Roo.each(this.picker().select('.next', true).elements, function(v){
18941             v.show();
18942             switch (this.viewMode) {
18943                 case 0:
18944
18945                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18946                         v.hide();
18947                     }
18948                     break;
18949                 case 1:
18950                 case 2:
18951                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18952                         v.hide();
18953                     }
18954                     break;
18955             }
18956         })
18957     },
18958     
18959     moveMonth: function(date, dir)
18960     {
18961         if (!dir) {
18962             return date;
18963         }
18964         var new_date = new Date(date.valueOf()),
18965         day = new_date.getUTCDate(),
18966         month = new_date.getUTCMonth(),
18967         mag = Math.abs(dir),
18968         new_month, test;
18969         dir = dir > 0 ? 1 : -1;
18970         if (mag == 1){
18971             test = dir == -1
18972             // If going back one month, make sure month is not current month
18973             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18974             ? function(){
18975                 return new_date.getUTCMonth() == month;
18976             }
18977             // If going forward one month, make sure month is as expected
18978             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18979             : function(){
18980                 return new_date.getUTCMonth() != new_month;
18981             };
18982             new_month = month + dir;
18983             new_date.setUTCMonth(new_month);
18984             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18985             if (new_month < 0 || new_month > 11) {
18986                 new_month = (new_month + 12) % 12;
18987             }
18988         } else {
18989             // For magnitudes >1, move one month at a time...
18990             for (var i=0; i<mag; i++) {
18991                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18992                 new_date = this.moveMonth(new_date, dir);
18993             }
18994             // ...then reset the day, keeping it in the new month
18995             new_month = new_date.getUTCMonth();
18996             new_date.setUTCDate(day);
18997             test = function(){
18998                 return new_month != new_date.getUTCMonth();
18999             };
19000         }
19001         // Common date-resetting loop -- if date is beyond end of month, make it
19002         // end of month
19003         while (test()){
19004             new_date.setUTCDate(--day);
19005             new_date.setUTCMonth(new_month);
19006         }
19007         return new_date;
19008     },
19009
19010     moveYear: function(date, dir)
19011     {
19012         return this.moveMonth(date, dir*12);
19013     },
19014
19015     dateWithinRange: function(date)
19016     {
19017         return date >= this.startDate && date <= this.endDate;
19018     },
19019
19020     
19021     remove: function() 
19022     {
19023         this.picker().remove();
19024     },
19025     
19026     validateValue : function(value)
19027     {
19028         if(value.length < 1)  {
19029             if(this.allowBlank){
19030                 return true;
19031             }
19032             return false;
19033         }
19034         
19035         if(value.length < this.minLength){
19036             return false;
19037         }
19038         if(value.length > this.maxLength){
19039             return false;
19040         }
19041         if(this.vtype){
19042             var vt = Roo.form.VTypes;
19043             if(!vt[this.vtype](value, this)){
19044                 return false;
19045             }
19046         }
19047         if(typeof this.validator == "function"){
19048             var msg = this.validator(value);
19049             if(msg !== true){
19050                 return false;
19051             }
19052         }
19053         
19054         if(this.regex && !this.regex.test(value)){
19055             return false;
19056         }
19057         
19058         if(typeof(this.parseDate(value)) == 'undefined'){
19059             return false;
19060         }
19061         
19062         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19063             return false;
19064         }      
19065         
19066         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19067             return false;
19068         } 
19069         
19070         
19071         return true;
19072     }
19073    
19074 });
19075
19076 Roo.apply(Roo.bootstrap.DateField,  {
19077     
19078     head : {
19079         tag: 'thead',
19080         cn: [
19081         {
19082             tag: 'tr',
19083             cn: [
19084             {
19085                 tag: 'th',
19086                 cls: 'prev',
19087                 html: '<i class="fa fa-arrow-left"/>'
19088             },
19089             {
19090                 tag: 'th',
19091                 cls: 'switch',
19092                 colspan: '5'
19093             },
19094             {
19095                 tag: 'th',
19096                 cls: 'next',
19097                 html: '<i class="fa fa-arrow-right"/>'
19098             }
19099
19100             ]
19101         }
19102         ]
19103     },
19104     
19105     content : {
19106         tag: 'tbody',
19107         cn: [
19108         {
19109             tag: 'tr',
19110             cn: [
19111             {
19112                 tag: 'td',
19113                 colspan: '7'
19114             }
19115             ]
19116         }
19117         ]
19118     },
19119     
19120     footer : {
19121         tag: 'tfoot',
19122         cn: [
19123         {
19124             tag: 'tr',
19125             cn: [
19126             {
19127                 tag: 'th',
19128                 colspan: '7',
19129                 cls: 'today'
19130             }
19131                     
19132             ]
19133         }
19134         ]
19135     },
19136     
19137     dates:{
19138         en: {
19139             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19140             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19141             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19142             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19143             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19144             today: "Today"
19145         }
19146     },
19147     
19148     modes: [
19149     {
19150         clsName: 'days',
19151         navFnc: 'Month',
19152         navStep: 1
19153     },
19154     {
19155         clsName: 'months',
19156         navFnc: 'FullYear',
19157         navStep: 1
19158     },
19159     {
19160         clsName: 'years',
19161         navFnc: 'FullYear',
19162         navStep: 10
19163     }]
19164 });
19165
19166 Roo.apply(Roo.bootstrap.DateField,  {
19167   
19168     template : {
19169         tag: 'div',
19170         cls: 'datepicker dropdown-menu roo-dynamic',
19171         cn: [
19172         {
19173             tag: 'div',
19174             cls: 'datepicker-days',
19175             cn: [
19176             {
19177                 tag: 'table',
19178                 cls: 'table-condensed',
19179                 cn:[
19180                 Roo.bootstrap.DateField.head,
19181                 {
19182                     tag: 'tbody'
19183                 },
19184                 Roo.bootstrap.DateField.footer
19185                 ]
19186             }
19187             ]
19188         },
19189         {
19190             tag: 'div',
19191             cls: 'datepicker-months',
19192             cn: [
19193             {
19194                 tag: 'table',
19195                 cls: 'table-condensed',
19196                 cn:[
19197                 Roo.bootstrap.DateField.head,
19198                 Roo.bootstrap.DateField.content,
19199                 Roo.bootstrap.DateField.footer
19200                 ]
19201             }
19202             ]
19203         },
19204         {
19205             tag: 'div',
19206             cls: 'datepicker-years',
19207             cn: [
19208             {
19209                 tag: 'table',
19210                 cls: 'table-condensed',
19211                 cn:[
19212                 Roo.bootstrap.DateField.head,
19213                 Roo.bootstrap.DateField.content,
19214                 Roo.bootstrap.DateField.footer
19215                 ]
19216             }
19217             ]
19218         }
19219         ]
19220     }
19221 });
19222
19223  
19224
19225  /*
19226  * - LGPL
19227  *
19228  * TimeField
19229  * 
19230  */
19231
19232 /**
19233  * @class Roo.bootstrap.TimeField
19234  * @extends Roo.bootstrap.Input
19235  * Bootstrap DateField class
19236  * 
19237  * 
19238  * @constructor
19239  * Create a new TimeField
19240  * @param {Object} config The config object
19241  */
19242
19243 Roo.bootstrap.TimeField = function(config){
19244     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19245     this.addEvents({
19246             /**
19247              * @event show
19248              * Fires when this field show.
19249              * @param {Roo.bootstrap.DateField} thisthis
19250              * @param {Mixed} date The date value
19251              */
19252             show : true,
19253             /**
19254              * @event show
19255              * Fires when this field hide.
19256              * @param {Roo.bootstrap.DateField} this
19257              * @param {Mixed} date The date value
19258              */
19259             hide : true,
19260             /**
19261              * @event select
19262              * Fires when select a date.
19263              * @param {Roo.bootstrap.DateField} this
19264              * @param {Mixed} date The date value
19265              */
19266             select : true
19267         });
19268 };
19269
19270 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19271     
19272     /**
19273      * @cfg {String} format
19274      * The default time format string which can be overriden for localization support.  The format must be
19275      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19276      */
19277     format : "H:i",
19278        
19279     onRender: function(ct, position)
19280     {
19281         
19282         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19283                 
19284         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19285         
19286         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19287         
19288         this.pop = this.picker().select('>.datepicker-time',true).first();
19289         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19290         
19291         this.picker().on('mousedown', this.onMousedown, this);
19292         this.picker().on('click', this.onClick, this);
19293         
19294         this.picker().addClass('datepicker-dropdown');
19295     
19296         this.fillTime();
19297         this.update();
19298             
19299         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19300         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19301         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19302         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19303         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19304         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19305
19306     },
19307     
19308     fireKey: function(e){
19309         if (!this.picker().isVisible()){
19310             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19311                 this.show();
19312             }
19313             return;
19314         }
19315
19316         e.preventDefault();
19317         
19318         switch(e.keyCode){
19319             case 27: // escape
19320                 this.hide();
19321                 break;
19322             case 37: // left
19323             case 39: // right
19324                 this.onTogglePeriod();
19325                 break;
19326             case 38: // up
19327                 this.onIncrementMinutes();
19328                 break;
19329             case 40: // down
19330                 this.onDecrementMinutes();
19331                 break;
19332             case 13: // enter
19333             case 9: // tab
19334                 this.setTime();
19335                 break;
19336         }
19337     },
19338     
19339     onClick: function(e) {
19340         e.stopPropagation();
19341         e.preventDefault();
19342     },
19343     
19344     picker : function()
19345     {
19346         return this.el.select('.datepicker', true).first();
19347     },
19348     
19349     fillTime: function()
19350     {    
19351         var time = this.pop.select('tbody', true).first();
19352         
19353         time.dom.innerHTML = '';
19354         
19355         time.createChild({
19356             tag: 'tr',
19357             cn: [
19358                 {
19359                     tag: 'td',
19360                     cn: [
19361                         {
19362                             tag: 'a',
19363                             href: '#',
19364                             cls: 'btn',
19365                             cn: [
19366                                 {
19367                                     tag: 'span',
19368                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19369                                 }
19370                             ]
19371                         } 
19372                     ]
19373                 },
19374                 {
19375                     tag: 'td',
19376                     cls: 'separator'
19377                 },
19378                 {
19379                     tag: 'td',
19380                     cn: [
19381                         {
19382                             tag: 'a',
19383                             href: '#',
19384                             cls: 'btn',
19385                             cn: [
19386                                 {
19387                                     tag: 'span',
19388                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19389                                 }
19390                             ]
19391                         }
19392                     ]
19393                 },
19394                 {
19395                     tag: 'td',
19396                     cls: 'separator'
19397                 }
19398             ]
19399         });
19400         
19401         time.createChild({
19402             tag: 'tr',
19403             cn: [
19404                 {
19405                     tag: 'td',
19406                     cn: [
19407                         {
19408                             tag: 'span',
19409                             cls: 'timepicker-hour',
19410                             html: '00'
19411                         }  
19412                     ]
19413                 },
19414                 {
19415                     tag: 'td',
19416                     cls: 'separator',
19417                     html: ':'
19418                 },
19419                 {
19420                     tag: 'td',
19421                     cn: [
19422                         {
19423                             tag: 'span',
19424                             cls: 'timepicker-minute',
19425                             html: '00'
19426                         }  
19427                     ]
19428                 },
19429                 {
19430                     tag: 'td',
19431                     cls: 'separator'
19432                 },
19433                 {
19434                     tag: 'td',
19435                     cn: [
19436                         {
19437                             tag: 'button',
19438                             type: 'button',
19439                             cls: 'btn btn-primary period',
19440                             html: 'AM'
19441                             
19442                         }
19443                     ]
19444                 }
19445             ]
19446         });
19447         
19448         time.createChild({
19449             tag: 'tr',
19450             cn: [
19451                 {
19452                     tag: 'td',
19453                     cn: [
19454                         {
19455                             tag: 'a',
19456                             href: '#',
19457                             cls: 'btn',
19458                             cn: [
19459                                 {
19460                                     tag: 'span',
19461                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19462                                 }
19463                             ]
19464                         }
19465                     ]
19466                 },
19467                 {
19468                     tag: 'td',
19469                     cls: 'separator'
19470                 },
19471                 {
19472                     tag: 'td',
19473                     cn: [
19474                         {
19475                             tag: 'a',
19476                             href: '#',
19477                             cls: 'btn',
19478                             cn: [
19479                                 {
19480                                     tag: 'span',
19481                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19482                                 }
19483                             ]
19484                         }
19485                     ]
19486                 },
19487                 {
19488                     tag: 'td',
19489                     cls: 'separator'
19490                 }
19491             ]
19492         });
19493         
19494     },
19495     
19496     update: function()
19497     {
19498         
19499         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19500         
19501         this.fill();
19502     },
19503     
19504     fill: function() 
19505     {
19506         var hours = this.time.getHours();
19507         var minutes = this.time.getMinutes();
19508         var period = 'AM';
19509         
19510         if(hours > 11){
19511             period = 'PM';
19512         }
19513         
19514         if(hours == 0){
19515             hours = 12;
19516         }
19517         
19518         
19519         if(hours > 12){
19520             hours = hours - 12;
19521         }
19522         
19523         if(hours < 10){
19524             hours = '0' + hours;
19525         }
19526         
19527         if(minutes < 10){
19528             minutes = '0' + minutes;
19529         }
19530         
19531         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19532         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19533         this.pop.select('button', true).first().dom.innerHTML = period;
19534         
19535     },
19536     
19537     place: function()
19538     {   
19539         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19540         
19541         var cls = ['bottom'];
19542         
19543         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19544             cls.pop();
19545             cls.push('top');
19546         }
19547         
19548         cls.push('right');
19549         
19550         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19551             cls.pop();
19552             cls.push('left');
19553         }
19554         
19555         this.picker().addClass(cls.join('-'));
19556         
19557         var _this = this;
19558         
19559         Roo.each(cls, function(c){
19560             if(c == 'bottom'){
19561                 _this.picker().setTop(_this.inputEl().getHeight());
19562                 return;
19563             }
19564             if(c == 'top'){
19565                 _this.picker().setTop(0 - _this.picker().getHeight());
19566                 return;
19567             }
19568             
19569             if(c == 'left'){
19570                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19571                 return;
19572             }
19573             if(c == 'right'){
19574                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19575                 return;
19576             }
19577         });
19578         
19579     },
19580   
19581     onFocus : function()
19582     {
19583         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19584         this.show();
19585     },
19586     
19587     onBlur : function()
19588     {
19589         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19590         this.hide();
19591     },
19592     
19593     show : function()
19594     {
19595         this.picker().show();
19596         this.pop.show();
19597         this.update();
19598         this.place();
19599         
19600         this.fireEvent('show', this, this.date);
19601     },
19602     
19603     hide : function()
19604     {
19605         this.picker().hide();
19606         this.pop.hide();
19607         
19608         this.fireEvent('hide', this, this.date);
19609     },
19610     
19611     setTime : function()
19612     {
19613         this.hide();
19614         this.setValue(this.time.format(this.format));
19615         
19616         this.fireEvent('select', this, this.date);
19617         
19618         
19619     },
19620     
19621     onMousedown: function(e){
19622         e.stopPropagation();
19623         e.preventDefault();
19624     },
19625     
19626     onIncrementHours: function()
19627     {
19628         Roo.log('onIncrementHours');
19629         this.time = this.time.add(Date.HOUR, 1);
19630         this.update();
19631         
19632     },
19633     
19634     onDecrementHours: function()
19635     {
19636         Roo.log('onDecrementHours');
19637         this.time = this.time.add(Date.HOUR, -1);
19638         this.update();
19639     },
19640     
19641     onIncrementMinutes: function()
19642     {
19643         Roo.log('onIncrementMinutes');
19644         this.time = this.time.add(Date.MINUTE, 1);
19645         this.update();
19646     },
19647     
19648     onDecrementMinutes: function()
19649     {
19650         Roo.log('onDecrementMinutes');
19651         this.time = this.time.add(Date.MINUTE, -1);
19652         this.update();
19653     },
19654     
19655     onTogglePeriod: function()
19656     {
19657         Roo.log('onTogglePeriod');
19658         this.time = this.time.add(Date.HOUR, 12);
19659         this.update();
19660     }
19661     
19662    
19663 });
19664
19665 Roo.apply(Roo.bootstrap.TimeField,  {
19666     
19667     content : {
19668         tag: 'tbody',
19669         cn: [
19670             {
19671                 tag: 'tr',
19672                 cn: [
19673                 {
19674                     tag: 'td',
19675                     colspan: '7'
19676                 }
19677                 ]
19678             }
19679         ]
19680     },
19681     
19682     footer : {
19683         tag: 'tfoot',
19684         cn: [
19685             {
19686                 tag: 'tr',
19687                 cn: [
19688                 {
19689                     tag: 'th',
19690                     colspan: '7',
19691                     cls: '',
19692                     cn: [
19693                         {
19694                             tag: 'button',
19695                             cls: 'btn btn-info ok',
19696                             html: 'OK'
19697                         }
19698                     ]
19699                 }
19700
19701                 ]
19702             }
19703         ]
19704     }
19705 });
19706
19707 Roo.apply(Roo.bootstrap.TimeField,  {
19708   
19709     template : {
19710         tag: 'div',
19711         cls: 'datepicker dropdown-menu',
19712         cn: [
19713             {
19714                 tag: 'div',
19715                 cls: 'datepicker-time',
19716                 cn: [
19717                 {
19718                     tag: 'table',
19719                     cls: 'table-condensed',
19720                     cn:[
19721                     Roo.bootstrap.TimeField.content,
19722                     Roo.bootstrap.TimeField.footer
19723                     ]
19724                 }
19725                 ]
19726             }
19727         ]
19728     }
19729 });
19730
19731  
19732
19733  /*
19734  * - LGPL
19735  *
19736  * MonthField
19737  * 
19738  */
19739
19740 /**
19741  * @class Roo.bootstrap.MonthField
19742  * @extends Roo.bootstrap.Input
19743  * Bootstrap MonthField class
19744  * 
19745  * @cfg {String} language default en
19746  * 
19747  * @constructor
19748  * Create a new MonthField
19749  * @param {Object} config The config object
19750  */
19751
19752 Roo.bootstrap.MonthField = function(config){
19753     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19754     
19755     this.addEvents({
19756         /**
19757          * @event show
19758          * Fires when this field show.
19759          * @param {Roo.bootstrap.MonthField} this
19760          * @param {Mixed} date The date value
19761          */
19762         show : true,
19763         /**
19764          * @event show
19765          * Fires when this field hide.
19766          * @param {Roo.bootstrap.MonthField} this
19767          * @param {Mixed} date The date value
19768          */
19769         hide : true,
19770         /**
19771          * @event select
19772          * Fires when select a date.
19773          * @param {Roo.bootstrap.MonthField} this
19774          * @param {String} oldvalue The old value
19775          * @param {String} newvalue The new value
19776          */
19777         select : true
19778     });
19779 };
19780
19781 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19782     
19783     onRender: function(ct, position)
19784     {
19785         
19786         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19787         
19788         this.language = this.language || 'en';
19789         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19790         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19791         
19792         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19793         this.isInline = false;
19794         this.isInput = true;
19795         this.component = this.el.select('.add-on', true).first() || false;
19796         this.component = (this.component && this.component.length === 0) ? false : this.component;
19797         this.hasInput = this.component && this.inputEL().length;
19798         
19799         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19800         
19801         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19802         
19803         this.picker().on('mousedown', this.onMousedown, this);
19804         this.picker().on('click', this.onClick, this);
19805         
19806         this.picker().addClass('datepicker-dropdown');
19807         
19808         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19809             v.setStyle('width', '189px');
19810         });
19811         
19812         this.fillMonths();
19813         
19814         this.update();
19815         
19816         if(this.isInline) {
19817             this.show();
19818         }
19819         
19820     },
19821     
19822     setValue: function(v, suppressEvent)
19823     {   
19824         var o = this.getValue();
19825         
19826         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19827         
19828         this.update();
19829
19830         if(suppressEvent !== true){
19831             this.fireEvent('select', this, o, v);
19832         }
19833         
19834     },
19835     
19836     getValue: function()
19837     {
19838         return this.value;
19839     },
19840     
19841     onClick: function(e) 
19842     {
19843         e.stopPropagation();
19844         e.preventDefault();
19845         
19846         var target = e.getTarget();
19847         
19848         if(target.nodeName.toLowerCase() === 'i'){
19849             target = Roo.get(target).dom.parentNode;
19850         }
19851         
19852         var nodeName = target.nodeName;
19853         var className = target.className;
19854         var html = target.innerHTML;
19855         
19856         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19857             return;
19858         }
19859         
19860         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19861         
19862         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19863         
19864         this.hide();
19865                         
19866     },
19867     
19868     picker : function()
19869     {
19870         return this.pickerEl;
19871     },
19872     
19873     fillMonths: function()
19874     {    
19875         var i = 0;
19876         var months = this.picker().select('>.datepicker-months td', true).first();
19877         
19878         months.dom.innerHTML = '';
19879         
19880         while (i < 12) {
19881             var month = {
19882                 tag: 'span',
19883                 cls: 'month',
19884                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19885             };
19886             
19887             months.createChild(month);
19888         }
19889         
19890     },
19891     
19892     update: function()
19893     {
19894         var _this = this;
19895         
19896         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19897             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19898         }
19899         
19900         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19901             e.removeClass('active');
19902             
19903             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19904                 e.addClass('active');
19905             }
19906         })
19907     },
19908     
19909     place: function()
19910     {
19911         if(this.isInline) {
19912             return;
19913         }
19914         
19915         this.picker().removeClass(['bottom', 'top']);
19916         
19917         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19918             /*
19919              * place to the top of element!
19920              *
19921              */
19922             
19923             this.picker().addClass('top');
19924             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19925             
19926             return;
19927         }
19928         
19929         this.picker().addClass('bottom');
19930         
19931         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19932     },
19933     
19934     onFocus : function()
19935     {
19936         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19937         this.show();
19938     },
19939     
19940     onBlur : function()
19941     {
19942         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19943         
19944         var d = this.inputEl().getValue();
19945         
19946         this.setValue(d);
19947                 
19948         this.hide();
19949     },
19950     
19951     show : function()
19952     {
19953         this.picker().show();
19954         this.picker().select('>.datepicker-months', true).first().show();
19955         this.update();
19956         this.place();
19957         
19958         this.fireEvent('show', this, this.date);
19959     },
19960     
19961     hide : function()
19962     {
19963         if(this.isInline) {
19964             return;
19965         }
19966         this.picker().hide();
19967         this.fireEvent('hide', this, this.date);
19968         
19969     },
19970     
19971     onMousedown: function(e)
19972     {
19973         e.stopPropagation();
19974         e.preventDefault();
19975     },
19976     
19977     keyup: function(e)
19978     {
19979         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19980         this.update();
19981     },
19982
19983     fireKey: function(e)
19984     {
19985         if (!this.picker().isVisible()){
19986             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19987                 this.show();
19988             }
19989             return;
19990         }
19991         
19992         var dir;
19993         
19994         switch(e.keyCode){
19995             case 27: // escape
19996                 this.hide();
19997                 e.preventDefault();
19998                 break;
19999             case 37: // left
20000             case 39: // right
20001                 dir = e.keyCode == 37 ? -1 : 1;
20002                 
20003                 this.vIndex = this.vIndex + dir;
20004                 
20005                 if(this.vIndex < 0){
20006                     this.vIndex = 0;
20007                 }
20008                 
20009                 if(this.vIndex > 11){
20010                     this.vIndex = 11;
20011                 }
20012                 
20013                 if(isNaN(this.vIndex)){
20014                     this.vIndex = 0;
20015                 }
20016                 
20017                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20018                 
20019                 break;
20020             case 38: // up
20021             case 40: // down
20022                 
20023                 dir = e.keyCode == 38 ? -1 : 1;
20024                 
20025                 this.vIndex = this.vIndex + dir * 4;
20026                 
20027                 if(this.vIndex < 0){
20028                     this.vIndex = 0;
20029                 }
20030                 
20031                 if(this.vIndex > 11){
20032                     this.vIndex = 11;
20033                 }
20034                 
20035                 if(isNaN(this.vIndex)){
20036                     this.vIndex = 0;
20037                 }
20038                 
20039                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20040                 break;
20041                 
20042             case 13: // enter
20043                 
20044                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20045                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20046                 }
20047                 
20048                 this.hide();
20049                 e.preventDefault();
20050                 break;
20051             case 9: // tab
20052                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20053                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20054                 }
20055                 this.hide();
20056                 break;
20057             case 16: // shift
20058             case 17: // ctrl
20059             case 18: // alt
20060                 break;
20061             default :
20062                 this.hide();
20063                 
20064         }
20065     },
20066     
20067     remove: function() 
20068     {
20069         this.picker().remove();
20070     }
20071    
20072 });
20073
20074 Roo.apply(Roo.bootstrap.MonthField,  {
20075     
20076     content : {
20077         tag: 'tbody',
20078         cn: [
20079         {
20080             tag: 'tr',
20081             cn: [
20082             {
20083                 tag: 'td',
20084                 colspan: '7'
20085             }
20086             ]
20087         }
20088         ]
20089     },
20090     
20091     dates:{
20092         en: {
20093             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20094             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20095         }
20096     }
20097 });
20098
20099 Roo.apply(Roo.bootstrap.MonthField,  {
20100   
20101     template : {
20102         tag: 'div',
20103         cls: 'datepicker dropdown-menu roo-dynamic',
20104         cn: [
20105             {
20106                 tag: 'div',
20107                 cls: 'datepicker-months',
20108                 cn: [
20109                 {
20110                     tag: 'table',
20111                     cls: 'table-condensed',
20112                     cn:[
20113                         Roo.bootstrap.DateField.content
20114                     ]
20115                 }
20116                 ]
20117             }
20118         ]
20119     }
20120 });
20121
20122  
20123
20124  
20125  /*
20126  * - LGPL
20127  *
20128  * CheckBox
20129  * 
20130  */
20131
20132 /**
20133  * @class Roo.bootstrap.CheckBox
20134  * @extends Roo.bootstrap.Input
20135  * Bootstrap CheckBox class
20136  * 
20137  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20138  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20139  * @cfg {String} boxLabel The text that appears beside the checkbox
20140  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20141  * @cfg {Boolean} checked initnal the element
20142  * @cfg {Boolean} inline inline the element (default false)
20143  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20144  * @cfg {String} tooltip label tooltip
20145  * 
20146  * @constructor
20147  * Create a new CheckBox
20148  * @param {Object} config The config object
20149  */
20150
20151 Roo.bootstrap.CheckBox = function(config){
20152     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20153    
20154     this.addEvents({
20155         /**
20156         * @event check
20157         * Fires when the element is checked or unchecked.
20158         * @param {Roo.bootstrap.CheckBox} this This input
20159         * @param {Boolean} checked The new checked value
20160         */
20161        check : true,
20162        /**
20163         * @event click
20164         * Fires when the element is click.
20165         * @param {Roo.bootstrap.CheckBox} this This input
20166         */
20167        click : true
20168     });
20169     
20170 };
20171
20172 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20173   
20174     inputType: 'checkbox',
20175     inputValue: 1,
20176     valueOff: 0,
20177     boxLabel: false,
20178     checked: false,
20179     weight : false,
20180     inline: false,
20181     tooltip : '',
20182     
20183     getAutoCreate : function()
20184     {
20185         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20186         
20187         var id = Roo.id();
20188         
20189         var cfg = {};
20190         
20191         cfg.cls = 'form-group ' + this.inputType; //input-group
20192         
20193         if(this.inline){
20194             cfg.cls += ' ' + this.inputType + '-inline';
20195         }
20196         
20197         var input =  {
20198             tag: 'input',
20199             id : id,
20200             type : this.inputType,
20201             value : this.inputValue,
20202             cls : 'roo-' + this.inputType, //'form-box',
20203             placeholder : this.placeholder || ''
20204             
20205         };
20206         
20207         if(this.inputType != 'radio'){
20208             var hidden =  {
20209                 tag: 'input',
20210                 type : 'hidden',
20211                 cls : 'roo-hidden-value',
20212                 value : this.checked ? this.inputValue : this.valueOff
20213             };
20214         }
20215         
20216             
20217         if (this.weight) { // Validity check?
20218             cfg.cls += " " + this.inputType + "-" + this.weight;
20219         }
20220         
20221         if (this.disabled) {
20222             input.disabled=true;
20223         }
20224         
20225         if(this.checked){
20226             input.checked = this.checked;
20227         }
20228         
20229         if (this.name) {
20230             
20231             input.name = this.name;
20232             
20233             if(this.inputType != 'radio'){
20234                 hidden.name = this.name;
20235                 input.name = '_hidden_' + this.name;
20236             }
20237         }
20238         
20239         if (this.size) {
20240             input.cls += ' input-' + this.size;
20241         }
20242         
20243         var settings=this;
20244         
20245         ['xs','sm','md','lg'].map(function(size){
20246             if (settings[size]) {
20247                 cfg.cls += ' col-' + size + '-' + settings[size];
20248             }
20249         });
20250         
20251         var inputblock = input;
20252          
20253         if (this.before || this.after) {
20254             
20255             inputblock = {
20256                 cls : 'input-group',
20257                 cn :  [] 
20258             };
20259             
20260             if (this.before) {
20261                 inputblock.cn.push({
20262                     tag :'span',
20263                     cls : 'input-group-addon',
20264                     html : this.before
20265                 });
20266             }
20267             
20268             inputblock.cn.push(input);
20269             
20270             if(this.inputType != 'radio'){
20271                 inputblock.cn.push(hidden);
20272             }
20273             
20274             if (this.after) {
20275                 inputblock.cn.push({
20276                     tag :'span',
20277                     cls : 'input-group-addon',
20278                     html : this.after
20279                 });
20280             }
20281             
20282         }
20283         
20284         if (align ==='left' && this.fieldLabel.length) {
20285 //                Roo.log("left and has label");
20286             cfg.cn = [
20287                 {
20288                     tag: 'label',
20289                     'for' :  id,
20290                     cls : 'control-label',
20291                     html : this.fieldLabel
20292                 },
20293                 {
20294                     cls : "", 
20295                     cn: [
20296                         inputblock
20297                     ]
20298                 }
20299             ];
20300             
20301             if(this.labelWidth > 12){
20302                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20303             }
20304             
20305             if(this.labelWidth < 13 && this.labelmd == 0){
20306                 this.labelmd = this.labelWidth;
20307             }
20308             
20309             if(this.labellg > 0){
20310                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20311                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20312             }
20313             
20314             if(this.labelmd > 0){
20315                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20316                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20317             }
20318             
20319             if(this.labelsm > 0){
20320                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20321                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20322             }
20323             
20324             if(this.labelxs > 0){
20325                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20326                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20327             }
20328             
20329         } else if ( this.fieldLabel.length) {
20330 //                Roo.log(" label");
20331                 cfg.cn = [
20332                    
20333                     {
20334                         tag: this.boxLabel ? 'span' : 'label',
20335                         'for': id,
20336                         cls: 'control-label box-input-label',
20337                         //cls : 'input-group-addon',
20338                         html : this.fieldLabel
20339                     },
20340                     
20341                     inputblock
20342                     
20343                 ];
20344
20345         } else {
20346             
20347 //                Roo.log(" no label && no align");
20348                 cfg.cn = [  inputblock ] ;
20349                 
20350                 
20351         }
20352         
20353         if(this.boxLabel){
20354              var boxLabelCfg = {
20355                 tag: 'label',
20356                 //'for': id, // box label is handled by onclick - so no for...
20357                 cls: 'box-label',
20358                 html: this.boxLabel
20359             };
20360             
20361             if(this.tooltip){
20362                 boxLabelCfg.tooltip = this.tooltip;
20363             }
20364              
20365             cfg.cn.push(boxLabelCfg);
20366         }
20367         
20368         if(this.inputType != 'radio'){
20369             cfg.cn.push(hidden);
20370         }
20371         
20372         return cfg;
20373         
20374     },
20375     
20376     /**
20377      * return the real input element.
20378      */
20379     inputEl: function ()
20380     {
20381         return this.el.select('input.roo-' + this.inputType,true).first();
20382     },
20383     hiddenEl: function ()
20384     {
20385         return this.el.select('input.roo-hidden-value',true).first();
20386     },
20387     
20388     labelEl: function()
20389     {
20390         return this.el.select('label.control-label',true).first();
20391     },
20392     /* depricated... */
20393     
20394     label: function()
20395     {
20396         return this.labelEl();
20397     },
20398     
20399     boxLabelEl: function()
20400     {
20401         return this.el.select('label.box-label',true).first();
20402     },
20403     
20404     initEvents : function()
20405     {
20406 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20407         
20408         this.inputEl().on('click', this.onClick,  this);
20409         
20410         if (this.boxLabel) { 
20411             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20412         }
20413         
20414         this.startValue = this.getValue();
20415         
20416         if(this.groupId){
20417             Roo.bootstrap.CheckBox.register(this);
20418         }
20419     },
20420     
20421     onClick : function(e)
20422     {   
20423         if(this.fireEvent('click', this, e) !== false){
20424             this.setChecked(!this.checked);
20425         }
20426         
20427     },
20428     
20429     setChecked : function(state,suppressEvent)
20430     {
20431         this.startValue = this.getValue();
20432
20433         if(this.inputType == 'radio'){
20434             
20435             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20436                 e.dom.checked = false;
20437             });
20438             
20439             this.inputEl().dom.checked = true;
20440             
20441             this.inputEl().dom.value = this.inputValue;
20442             
20443             if(suppressEvent !== true){
20444                 this.fireEvent('check', this, true);
20445             }
20446             
20447             this.validate();
20448             
20449             return;
20450         }
20451         
20452         this.checked = state;
20453         
20454         this.inputEl().dom.checked = state;
20455         
20456         
20457         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20458         
20459         if(suppressEvent !== true){
20460             this.fireEvent('check', this, state);
20461         }
20462         
20463         this.validate();
20464     },
20465     
20466     getValue : function()
20467     {
20468         if(this.inputType == 'radio'){
20469             return this.getGroupValue();
20470         }
20471         
20472         return this.hiddenEl().dom.value;
20473         
20474     },
20475     
20476     getGroupValue : function()
20477     {
20478         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20479             return '';
20480         }
20481         
20482         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20483     },
20484     
20485     setValue : function(v,suppressEvent)
20486     {
20487         if(this.inputType == 'radio'){
20488             this.setGroupValue(v, suppressEvent);
20489             return;
20490         }
20491         
20492         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20493         
20494         this.validate();
20495     },
20496     
20497     setGroupValue : function(v, suppressEvent)
20498     {
20499         this.startValue = this.getValue();
20500         
20501         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20502             e.dom.checked = false;
20503             
20504             if(e.dom.value == v){
20505                 e.dom.checked = true;
20506             }
20507         });
20508         
20509         if(suppressEvent !== true){
20510             this.fireEvent('check', this, true);
20511         }
20512
20513         this.validate();
20514         
20515         return;
20516     },
20517     
20518     validate : function()
20519     {
20520         if(
20521                 this.disabled || 
20522                 (this.inputType == 'radio' && this.validateRadio()) ||
20523                 (this.inputType == 'checkbox' && this.validateCheckbox())
20524         ){
20525             this.markValid();
20526             return true;
20527         }
20528         
20529         this.markInvalid();
20530         return false;
20531     },
20532     
20533     validateRadio : function()
20534     {
20535         if(this.allowBlank){
20536             return true;
20537         }
20538         
20539         var valid = false;
20540         
20541         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20542             if(!e.dom.checked){
20543                 return;
20544             }
20545             
20546             valid = true;
20547             
20548             return false;
20549         });
20550         
20551         return valid;
20552     },
20553     
20554     validateCheckbox : function()
20555     {
20556         if(!this.groupId){
20557             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20558             //return (this.getValue() == this.inputValue) ? true : false;
20559         }
20560         
20561         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20562         
20563         if(!group){
20564             return false;
20565         }
20566         
20567         var r = false;
20568         
20569         for(var i in group){
20570             if(group[i].el.isVisible(true)){
20571                 r = false;
20572                 break;
20573             }
20574             
20575             r = true;
20576         }
20577         
20578         for(var i in group){
20579             if(r){
20580                 break;
20581             }
20582             
20583             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20584         }
20585         
20586         return r;
20587     },
20588     
20589     /**
20590      * Mark this field as valid
20591      */
20592     markValid : function()
20593     {
20594         var _this = this;
20595         
20596         this.fireEvent('valid', this);
20597         
20598         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20599         
20600         if(this.groupId){
20601             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20602         }
20603         
20604         if(label){
20605             label.markValid();
20606         }
20607
20608         if(this.inputType == 'radio'){
20609             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20610                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20611                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20612             });
20613             
20614             return;
20615         }
20616
20617         if(!this.groupId){
20618             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20619             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20620             return;
20621         }
20622         
20623         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20624         
20625         if(!group){
20626             return;
20627         }
20628         
20629         for(var i in group){
20630             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20631             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20632         }
20633     },
20634     
20635      /**
20636      * Mark this field as invalid
20637      * @param {String} msg The validation message
20638      */
20639     markInvalid : function(msg)
20640     {
20641         if(this.allowBlank){
20642             return;
20643         }
20644         
20645         var _this = this;
20646         
20647         this.fireEvent('invalid', this, msg);
20648         
20649         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20650         
20651         if(this.groupId){
20652             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20653         }
20654         
20655         if(label){
20656             label.markInvalid();
20657         }
20658             
20659         if(this.inputType == 'radio'){
20660             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20661                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20662                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20663             });
20664             
20665             return;
20666         }
20667         
20668         if(!this.groupId){
20669             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20670             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20671             return;
20672         }
20673         
20674         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20675         
20676         if(!group){
20677             return;
20678         }
20679         
20680         for(var i in group){
20681             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20682             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20683         }
20684         
20685     },
20686     
20687     clearInvalid : function()
20688     {
20689         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20690         
20691         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20692         
20693         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20694         
20695         if (label && label.iconEl) {
20696             label.iconEl.removeClass(label.validClass);
20697             label.iconEl.removeClass(label.invalidClass);
20698         }
20699     },
20700     
20701     disable : function()
20702     {
20703         if(this.inputType != 'radio'){
20704             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20705             return;
20706         }
20707         
20708         var _this = this;
20709         
20710         if(this.rendered){
20711             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20712                 _this.getActionEl().addClass(this.disabledClass);
20713                 e.dom.disabled = true;
20714             });
20715         }
20716         
20717         this.disabled = true;
20718         this.fireEvent("disable", this);
20719         return this;
20720     },
20721
20722     enable : function()
20723     {
20724         if(this.inputType != 'radio'){
20725             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20726             return;
20727         }
20728         
20729         var _this = this;
20730         
20731         if(this.rendered){
20732             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20733                 _this.getActionEl().removeClass(this.disabledClass);
20734                 e.dom.disabled = false;
20735             });
20736         }
20737         
20738         this.disabled = false;
20739         this.fireEvent("enable", this);
20740         return this;
20741     }
20742
20743 });
20744
20745 Roo.apply(Roo.bootstrap.CheckBox, {
20746     
20747     groups: {},
20748     
20749      /**
20750     * register a CheckBox Group
20751     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20752     */
20753     register : function(checkbox)
20754     {
20755         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20756             this.groups[checkbox.groupId] = {};
20757         }
20758         
20759         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20760             return;
20761         }
20762         
20763         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20764         
20765     },
20766     /**
20767     * fetch a CheckBox Group based on the group ID
20768     * @param {string} the group ID
20769     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20770     */
20771     get: function(groupId) {
20772         if (typeof(this.groups[groupId]) == 'undefined') {
20773             return false;
20774         }
20775         
20776         return this.groups[groupId] ;
20777     }
20778     
20779     
20780 });
20781 /*
20782  * - LGPL
20783  *
20784  * RadioItem
20785  * 
20786  */
20787
20788 /**
20789  * @class Roo.bootstrap.Radio
20790  * @extends Roo.bootstrap.Component
20791  * Bootstrap Radio class
20792  * @cfg {String} boxLabel - the label associated
20793  * @cfg {String} value - the value of radio
20794  * 
20795  * @constructor
20796  * Create a new Radio
20797  * @param {Object} config The config object
20798  */
20799 Roo.bootstrap.Radio = function(config){
20800     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20801     
20802 };
20803
20804 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20805     
20806     boxLabel : '',
20807     
20808     value : '',
20809     
20810     getAutoCreate : function()
20811     {
20812         var cfg = {
20813             tag : 'div',
20814             cls : 'form-group radio',
20815             cn : [
20816                 {
20817                     tag : 'label',
20818                     cls : 'box-label',
20819                     html : this.boxLabel
20820                 }
20821             ]
20822         };
20823         
20824         return cfg;
20825     },
20826     
20827     initEvents : function() 
20828     {
20829         this.parent().register(this);
20830         
20831         this.el.on('click', this.onClick, this);
20832         
20833     },
20834     
20835     onClick : function()
20836     {
20837         this.setChecked(true);
20838     },
20839     
20840     setChecked : function(state, suppressEvent)
20841     {
20842         this.parent().setValue(this.value, suppressEvent);
20843         
20844     },
20845     
20846     setBoxLabel : function(v)
20847     {
20848         this.boxLabel = v;
20849         
20850         if(this.rendered){
20851             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20852         }
20853     }
20854     
20855 });
20856  
20857
20858  /*
20859  * - LGPL
20860  *
20861  * Input
20862  * 
20863  */
20864
20865 /**
20866  * @class Roo.bootstrap.SecurePass
20867  * @extends Roo.bootstrap.Input
20868  * Bootstrap SecurePass class
20869  *
20870  * 
20871  * @constructor
20872  * Create a new SecurePass
20873  * @param {Object} config The config object
20874  */
20875  
20876 Roo.bootstrap.SecurePass = function (config) {
20877     // these go here, so the translation tool can replace them..
20878     this.errors = {
20879         PwdEmpty: "Please type a password, and then retype it to confirm.",
20880         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20881         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20882         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20883         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20884         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20885         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20886         TooWeak: "Your password is Too Weak."
20887     },
20888     this.meterLabel = "Password strength:";
20889     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20890     this.meterClass = [
20891         "roo-password-meter-tooweak", 
20892         "roo-password-meter-weak", 
20893         "roo-password-meter-medium", 
20894         "roo-password-meter-strong", 
20895         "roo-password-meter-grey"
20896     ];
20897     
20898     this.errors = {};
20899     
20900     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20901 }
20902
20903 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20904     /**
20905      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20906      * {
20907      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20908      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20909      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20910      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20911      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20912      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20913      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20914      * })
20915      */
20916     // private
20917     
20918     meterWidth: 300,
20919     errorMsg :'',    
20920     errors: false,
20921     imageRoot: '/',
20922     /**
20923      * @cfg {String/Object} Label for the strength meter (defaults to
20924      * 'Password strength:')
20925      */
20926     // private
20927     meterLabel: '',
20928     /**
20929      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20930      * ['Weak', 'Medium', 'Strong'])
20931      */
20932     // private    
20933     pwdStrengths: false,    
20934     // private
20935     strength: 0,
20936     // private
20937     _lastPwd: null,
20938     // private
20939     kCapitalLetter: 0,
20940     kSmallLetter: 1,
20941     kDigit: 2,
20942     kPunctuation: 3,
20943     
20944     insecure: false,
20945     // private
20946     initEvents: function ()
20947     {
20948         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20949
20950         if (this.el.is('input[type=password]') && Roo.isSafari) {
20951             this.el.on('keydown', this.SafariOnKeyDown, this);
20952         }
20953
20954         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20955     },
20956     // private
20957     onRender: function (ct, position)
20958     {
20959         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20960         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20961         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20962
20963         this.trigger.createChild({
20964                    cn: [
20965                     {
20966                     //id: 'PwdMeter',
20967                     tag: 'div',
20968                     cls: 'roo-password-meter-grey col-xs-12',
20969                     style: {
20970                         //width: 0,
20971                         //width: this.meterWidth + 'px'                                                
20972                         }
20973                     },
20974                     {                            
20975                          cls: 'roo-password-meter-text'                          
20976                     }
20977                 ]            
20978         });
20979
20980          
20981         if (this.hideTrigger) {
20982             this.trigger.setDisplayed(false);
20983         }
20984         this.setSize(this.width || '', this.height || '');
20985     },
20986     // private
20987     onDestroy: function ()
20988     {
20989         if (this.trigger) {
20990             this.trigger.removeAllListeners();
20991             this.trigger.remove();
20992         }
20993         if (this.wrap) {
20994             this.wrap.remove();
20995         }
20996         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20997     },
20998     // private
20999     checkStrength: function ()
21000     {
21001         var pwd = this.inputEl().getValue();
21002         if (pwd == this._lastPwd) {
21003             return;
21004         }
21005
21006         var strength;
21007         if (this.ClientSideStrongPassword(pwd)) {
21008             strength = 3;
21009         } else if (this.ClientSideMediumPassword(pwd)) {
21010             strength = 2;
21011         } else if (this.ClientSideWeakPassword(pwd)) {
21012             strength = 1;
21013         } else {
21014             strength = 0;
21015         }
21016         
21017         Roo.log('strength1: ' + strength);
21018         
21019         //var pm = this.trigger.child('div/div/div').dom;
21020         var pm = this.trigger.child('div/div');
21021         pm.removeClass(this.meterClass);
21022         pm.addClass(this.meterClass[strength]);
21023                 
21024         
21025         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21026                 
21027         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21028         
21029         this._lastPwd = pwd;
21030     },
21031     reset: function ()
21032     {
21033         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21034         
21035         this._lastPwd = '';
21036         
21037         var pm = this.trigger.child('div/div');
21038         pm.removeClass(this.meterClass);
21039         pm.addClass('roo-password-meter-grey');        
21040         
21041         
21042         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21043         
21044         pt.innerHTML = '';
21045         this.inputEl().dom.type='password';
21046     },
21047     // private
21048     validateValue: function (value)
21049     {
21050         
21051         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21052             return false;
21053         }
21054         if (value.length == 0) {
21055             if (this.allowBlank) {
21056                 this.clearInvalid();
21057                 return true;
21058             }
21059
21060             this.markInvalid(this.errors.PwdEmpty);
21061             this.errorMsg = this.errors.PwdEmpty;
21062             return false;
21063         }
21064         
21065         if(this.insecure){
21066             return true;
21067         }
21068         
21069         if ('[\x21-\x7e]*'.match(value)) {
21070             this.markInvalid(this.errors.PwdBadChar);
21071             this.errorMsg = this.errors.PwdBadChar;
21072             return false;
21073         }
21074         if (value.length < 6) {
21075             this.markInvalid(this.errors.PwdShort);
21076             this.errorMsg = this.errors.PwdShort;
21077             return false;
21078         }
21079         if (value.length > 16) {
21080             this.markInvalid(this.errors.PwdLong);
21081             this.errorMsg = this.errors.PwdLong;
21082             return false;
21083         }
21084         var strength;
21085         if (this.ClientSideStrongPassword(value)) {
21086             strength = 3;
21087         } else if (this.ClientSideMediumPassword(value)) {
21088             strength = 2;
21089         } else if (this.ClientSideWeakPassword(value)) {
21090             strength = 1;
21091         } else {
21092             strength = 0;
21093         }
21094
21095         
21096         if (strength < 2) {
21097             //this.markInvalid(this.errors.TooWeak);
21098             this.errorMsg = this.errors.TooWeak;
21099             //return false;
21100         }
21101         
21102         
21103         console.log('strength2: ' + strength);
21104         
21105         //var pm = this.trigger.child('div/div/div').dom;
21106         
21107         var pm = this.trigger.child('div/div');
21108         pm.removeClass(this.meterClass);
21109         pm.addClass(this.meterClass[strength]);
21110                 
21111         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21112                 
21113         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21114         
21115         this.errorMsg = ''; 
21116         return true;
21117     },
21118     // private
21119     CharacterSetChecks: function (type)
21120     {
21121         this.type = type;
21122         this.fResult = false;
21123     },
21124     // private
21125     isctype: function (character, type)
21126     {
21127         switch (type) {  
21128             case this.kCapitalLetter:
21129                 if (character >= 'A' && character <= 'Z') {
21130                     return true;
21131                 }
21132                 break;
21133             
21134             case this.kSmallLetter:
21135                 if (character >= 'a' && character <= 'z') {
21136                     return true;
21137                 }
21138                 break;
21139             
21140             case this.kDigit:
21141                 if (character >= '0' && character <= '9') {
21142                     return true;
21143                 }
21144                 break;
21145             
21146             case this.kPunctuation:
21147                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21148                     return true;
21149                 }
21150                 break;
21151             
21152             default:
21153                 return false;
21154         }
21155
21156     },
21157     // private
21158     IsLongEnough: function (pwd, size)
21159     {
21160         return !(pwd == null || isNaN(size) || pwd.length < size);
21161     },
21162     // private
21163     SpansEnoughCharacterSets: function (word, nb)
21164     {
21165         if (!this.IsLongEnough(word, nb))
21166         {
21167             return false;
21168         }
21169
21170         var characterSetChecks = new Array(
21171             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21172             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21173         );
21174         
21175         for (var index = 0; index < word.length; ++index) {
21176             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21177                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21178                     characterSetChecks[nCharSet].fResult = true;
21179                     break;
21180                 }
21181             }
21182         }
21183
21184         var nCharSets = 0;
21185         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21186             if (characterSetChecks[nCharSet].fResult) {
21187                 ++nCharSets;
21188             }
21189         }
21190
21191         if (nCharSets < nb) {
21192             return false;
21193         }
21194         return true;
21195     },
21196     // private
21197     ClientSideStrongPassword: function (pwd)
21198     {
21199         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21200     },
21201     // private
21202     ClientSideMediumPassword: function (pwd)
21203     {
21204         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21205     },
21206     // private
21207     ClientSideWeakPassword: function (pwd)
21208     {
21209         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21210     }
21211           
21212 })//<script type="text/javascript">
21213
21214 /*
21215  * Based  Ext JS Library 1.1.1
21216  * Copyright(c) 2006-2007, Ext JS, LLC.
21217  * LGPL
21218  *
21219  */
21220  
21221 /**
21222  * @class Roo.HtmlEditorCore
21223  * @extends Roo.Component
21224  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21225  *
21226  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21227  */
21228
21229 Roo.HtmlEditorCore = function(config){
21230     
21231     
21232     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21233     
21234     
21235     this.addEvents({
21236         /**
21237          * @event initialize
21238          * Fires when the editor is fully initialized (including the iframe)
21239          * @param {Roo.HtmlEditorCore} this
21240          */
21241         initialize: true,
21242         /**
21243          * @event activate
21244          * Fires when the editor is first receives the focus. Any insertion must wait
21245          * until after this event.
21246          * @param {Roo.HtmlEditorCore} this
21247          */
21248         activate: true,
21249          /**
21250          * @event beforesync
21251          * Fires before the textarea is updated with content from the editor iframe. Return false
21252          * to cancel the sync.
21253          * @param {Roo.HtmlEditorCore} this
21254          * @param {String} html
21255          */
21256         beforesync: true,
21257          /**
21258          * @event beforepush
21259          * Fires before the iframe editor is updated with content from the textarea. Return false
21260          * to cancel the push.
21261          * @param {Roo.HtmlEditorCore} this
21262          * @param {String} html
21263          */
21264         beforepush: true,
21265          /**
21266          * @event sync
21267          * Fires when the textarea is updated with content from the editor iframe.
21268          * @param {Roo.HtmlEditorCore} this
21269          * @param {String} html
21270          */
21271         sync: true,
21272          /**
21273          * @event push
21274          * Fires when the iframe editor is updated with content from the textarea.
21275          * @param {Roo.HtmlEditorCore} this
21276          * @param {String} html
21277          */
21278         push: true,
21279         
21280         /**
21281          * @event editorevent
21282          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21283          * @param {Roo.HtmlEditorCore} this
21284          */
21285         editorevent: true
21286         
21287     });
21288     
21289     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21290     
21291     // defaults : white / black...
21292     this.applyBlacklists();
21293     
21294     
21295     
21296 };
21297
21298
21299 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21300
21301
21302      /**
21303      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21304      */
21305     
21306     owner : false,
21307     
21308      /**
21309      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21310      *                        Roo.resizable.
21311      */
21312     resizable : false,
21313      /**
21314      * @cfg {Number} height (in pixels)
21315      */   
21316     height: 300,
21317    /**
21318      * @cfg {Number} width (in pixels)
21319      */   
21320     width: 500,
21321     
21322     /**
21323      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21324      * 
21325      */
21326     stylesheets: false,
21327     
21328     // id of frame..
21329     frameId: false,
21330     
21331     // private properties
21332     validationEvent : false,
21333     deferHeight: true,
21334     initialized : false,
21335     activated : false,
21336     sourceEditMode : false,
21337     onFocus : Roo.emptyFn,
21338     iframePad:3,
21339     hideMode:'offsets',
21340     
21341     clearUp: true,
21342     
21343     // blacklist + whitelisted elements..
21344     black: false,
21345     white: false,
21346      
21347     bodyCls : '',
21348
21349     /**
21350      * Protected method that will not generally be called directly. It
21351      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21352      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21353      */
21354     getDocMarkup : function(){
21355         // body styles..
21356         var st = '';
21357         
21358         // inherit styels from page...?? 
21359         if (this.stylesheets === false) {
21360             
21361             Roo.get(document.head).select('style').each(function(node) {
21362                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21363             });
21364             
21365             Roo.get(document.head).select('link').each(function(node) { 
21366                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21367             });
21368             
21369         } else if (!this.stylesheets.length) {
21370                 // simple..
21371                 st = '<style type="text/css">' +
21372                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21373                    '</style>';
21374         } else { 
21375             st = '<style type="text/css">' +
21376                     this.stylesheets +
21377                 '</style>';
21378         }
21379         
21380         st +=  '<style type="text/css">' +
21381             'IMG { cursor: pointer } ' +
21382         '</style>';
21383
21384         var cls = 'roo-htmleditor-body';
21385         
21386         if(this.bodyCls.length){
21387             cls += ' ' + this.bodyCls;
21388         }
21389         
21390         return '<html><head>' + st  +
21391             //<style type="text/css">' +
21392             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21393             //'</style>' +
21394             ' </head><body class="' +  cls + '"></body></html>';
21395     },
21396
21397     // private
21398     onRender : function(ct, position)
21399     {
21400         var _t = this;
21401         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21402         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21403         
21404         
21405         this.el.dom.style.border = '0 none';
21406         this.el.dom.setAttribute('tabIndex', -1);
21407         this.el.addClass('x-hidden hide');
21408         
21409         
21410         
21411         if(Roo.isIE){ // fix IE 1px bogus margin
21412             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21413         }
21414        
21415         
21416         this.frameId = Roo.id();
21417         
21418          
21419         
21420         var iframe = this.owner.wrap.createChild({
21421             tag: 'iframe',
21422             cls: 'form-control', // bootstrap..
21423             id: this.frameId,
21424             name: this.frameId,
21425             frameBorder : 'no',
21426             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21427         }, this.el
21428         );
21429         
21430         
21431         this.iframe = iframe.dom;
21432
21433          this.assignDocWin();
21434         
21435         this.doc.designMode = 'on';
21436        
21437         this.doc.open();
21438         this.doc.write(this.getDocMarkup());
21439         this.doc.close();
21440
21441         
21442         var task = { // must defer to wait for browser to be ready
21443             run : function(){
21444                 //console.log("run task?" + this.doc.readyState);
21445                 this.assignDocWin();
21446                 if(this.doc.body || this.doc.readyState == 'complete'){
21447                     try {
21448                         this.doc.designMode="on";
21449                     } catch (e) {
21450                         return;
21451                     }
21452                     Roo.TaskMgr.stop(task);
21453                     this.initEditor.defer(10, this);
21454                 }
21455             },
21456             interval : 10,
21457             duration: 10000,
21458             scope: this
21459         };
21460         Roo.TaskMgr.start(task);
21461
21462     },
21463
21464     // private
21465     onResize : function(w, h)
21466     {
21467          Roo.log('resize: ' +w + ',' + h );
21468         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21469         if(!this.iframe){
21470             return;
21471         }
21472         if(typeof w == 'number'){
21473             
21474             this.iframe.style.width = w + 'px';
21475         }
21476         if(typeof h == 'number'){
21477             
21478             this.iframe.style.height = h + 'px';
21479             if(this.doc){
21480                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21481             }
21482         }
21483         
21484     },
21485
21486     /**
21487      * Toggles the editor between standard and source edit mode.
21488      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21489      */
21490     toggleSourceEdit : function(sourceEditMode){
21491         
21492         this.sourceEditMode = sourceEditMode === true;
21493         
21494         if(this.sourceEditMode){
21495  
21496             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21497             
21498         }else{
21499             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21500             //this.iframe.className = '';
21501             this.deferFocus();
21502         }
21503         //this.setSize(this.owner.wrap.getSize());
21504         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21505     },
21506
21507     
21508   
21509
21510     /**
21511      * Protected method that will not generally be called directly. If you need/want
21512      * custom HTML cleanup, this is the method you should override.
21513      * @param {String} html The HTML to be cleaned
21514      * return {String} The cleaned HTML
21515      */
21516     cleanHtml : function(html){
21517         html = String(html);
21518         if(html.length > 5){
21519             if(Roo.isSafari){ // strip safari nonsense
21520                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21521             }
21522         }
21523         if(html == '&nbsp;'){
21524             html = '';
21525         }
21526         return html;
21527     },
21528
21529     /**
21530      * HTML Editor -> Textarea
21531      * Protected method that will not generally be called directly. Syncs the contents
21532      * of the editor iframe with the textarea.
21533      */
21534     syncValue : function(){
21535         if(this.initialized){
21536             var bd = (this.doc.body || this.doc.documentElement);
21537             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21538             var html = bd.innerHTML;
21539             if(Roo.isSafari){
21540                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21541                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21542                 if(m && m[1]){
21543                     html = '<div style="'+m[0]+'">' + html + '</div>';
21544                 }
21545             }
21546             html = this.cleanHtml(html);
21547             // fix up the special chars.. normaly like back quotes in word...
21548             // however we do not want to do this with chinese..
21549             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21550                 var cc = b.charCodeAt();
21551                 if (
21552                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21553                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21554                     (cc >= 0xf900 && cc < 0xfb00 )
21555                 ) {
21556                         return b;
21557                 }
21558                 return "&#"+cc+";" 
21559             });
21560             if(this.owner.fireEvent('beforesync', this, html) !== false){
21561                 this.el.dom.value = html;
21562                 this.owner.fireEvent('sync', this, html);
21563             }
21564         }
21565     },
21566
21567     /**
21568      * Protected method that will not generally be called directly. Pushes the value of the textarea
21569      * into the iframe editor.
21570      */
21571     pushValue : function(){
21572         if(this.initialized){
21573             var v = this.el.dom.value.trim();
21574             
21575 //            if(v.length < 1){
21576 //                v = '&#160;';
21577 //            }
21578             
21579             if(this.owner.fireEvent('beforepush', this, v) !== false){
21580                 var d = (this.doc.body || this.doc.documentElement);
21581                 d.innerHTML = v;
21582                 this.cleanUpPaste();
21583                 this.el.dom.value = d.innerHTML;
21584                 this.owner.fireEvent('push', this, v);
21585             }
21586         }
21587     },
21588
21589     // private
21590     deferFocus : function(){
21591         this.focus.defer(10, this);
21592     },
21593
21594     // doc'ed in Field
21595     focus : function(){
21596         if(this.win && !this.sourceEditMode){
21597             this.win.focus();
21598         }else{
21599             this.el.focus();
21600         }
21601     },
21602     
21603     assignDocWin: function()
21604     {
21605         var iframe = this.iframe;
21606         
21607          if(Roo.isIE){
21608             this.doc = iframe.contentWindow.document;
21609             this.win = iframe.contentWindow;
21610         } else {
21611 //            if (!Roo.get(this.frameId)) {
21612 //                return;
21613 //            }
21614 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21615 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21616             
21617             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21618                 return;
21619             }
21620             
21621             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21622             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21623         }
21624     },
21625     
21626     // private
21627     initEditor : function(){
21628         //console.log("INIT EDITOR");
21629         this.assignDocWin();
21630         
21631         
21632         
21633         this.doc.designMode="on";
21634         this.doc.open();
21635         this.doc.write(this.getDocMarkup());
21636         this.doc.close();
21637         
21638         var dbody = (this.doc.body || this.doc.documentElement);
21639         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21640         // this copies styles from the containing element into thsi one..
21641         // not sure why we need all of this..
21642         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21643         
21644         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21645         //ss['background-attachment'] = 'fixed'; // w3c
21646         dbody.bgProperties = 'fixed'; // ie
21647         //Roo.DomHelper.applyStyles(dbody, ss);
21648         Roo.EventManager.on(this.doc, {
21649             //'mousedown': this.onEditorEvent,
21650             'mouseup': this.onEditorEvent,
21651             'dblclick': this.onEditorEvent,
21652             'click': this.onEditorEvent,
21653             'keyup': this.onEditorEvent,
21654             buffer:100,
21655             scope: this
21656         });
21657         if(Roo.isGecko){
21658             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21659         }
21660         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21661             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21662         }
21663         this.initialized = true;
21664
21665         this.owner.fireEvent('initialize', this);
21666         this.pushValue();
21667     },
21668
21669     // private
21670     onDestroy : function(){
21671         
21672         
21673         
21674         if(this.rendered){
21675             
21676             //for (var i =0; i < this.toolbars.length;i++) {
21677             //    // fixme - ask toolbars for heights?
21678             //    this.toolbars[i].onDestroy();
21679            // }
21680             
21681             //this.wrap.dom.innerHTML = '';
21682             //this.wrap.remove();
21683         }
21684     },
21685
21686     // private
21687     onFirstFocus : function(){
21688         
21689         this.assignDocWin();
21690         
21691         
21692         this.activated = true;
21693          
21694     
21695         if(Roo.isGecko){ // prevent silly gecko errors
21696             this.win.focus();
21697             var s = this.win.getSelection();
21698             if(!s.focusNode || s.focusNode.nodeType != 3){
21699                 var r = s.getRangeAt(0);
21700                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21701                 r.collapse(true);
21702                 this.deferFocus();
21703             }
21704             try{
21705                 this.execCmd('useCSS', true);
21706                 this.execCmd('styleWithCSS', false);
21707             }catch(e){}
21708         }
21709         this.owner.fireEvent('activate', this);
21710     },
21711
21712     // private
21713     adjustFont: function(btn){
21714         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21715         //if(Roo.isSafari){ // safari
21716         //    adjust *= 2;
21717        // }
21718         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21719         if(Roo.isSafari){ // safari
21720             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21721             v =  (v < 10) ? 10 : v;
21722             v =  (v > 48) ? 48 : v;
21723             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21724             
21725         }
21726         
21727         
21728         v = Math.max(1, v+adjust);
21729         
21730         this.execCmd('FontSize', v  );
21731     },
21732
21733     onEditorEvent : function(e)
21734     {
21735         this.owner.fireEvent('editorevent', this, e);
21736       //  this.updateToolbar();
21737         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21738     },
21739
21740     insertTag : function(tg)
21741     {
21742         // could be a bit smarter... -> wrap the current selected tRoo..
21743         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21744             
21745             range = this.createRange(this.getSelection());
21746             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21747             wrappingNode.appendChild(range.extractContents());
21748             range.insertNode(wrappingNode);
21749
21750             return;
21751             
21752             
21753             
21754         }
21755         this.execCmd("formatblock",   tg);
21756         
21757     },
21758     
21759     insertText : function(txt)
21760     {
21761         
21762         
21763         var range = this.createRange();
21764         range.deleteContents();
21765                //alert(Sender.getAttribute('label'));
21766                
21767         range.insertNode(this.doc.createTextNode(txt));
21768     } ,
21769     
21770      
21771
21772     /**
21773      * Executes a Midas editor command on the editor document and performs necessary focus and
21774      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21775      * @param {String} cmd The Midas command
21776      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21777      */
21778     relayCmd : function(cmd, value){
21779         this.win.focus();
21780         this.execCmd(cmd, value);
21781         this.owner.fireEvent('editorevent', this);
21782         //this.updateToolbar();
21783         this.owner.deferFocus();
21784     },
21785
21786     /**
21787      * Executes a Midas editor command directly on the editor document.
21788      * For visual commands, you should use {@link #relayCmd} instead.
21789      * <b>This should only be called after the editor is initialized.</b>
21790      * @param {String} cmd The Midas command
21791      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21792      */
21793     execCmd : function(cmd, value){
21794         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21795         this.syncValue();
21796     },
21797  
21798  
21799    
21800     /**
21801      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21802      * to insert tRoo.
21803      * @param {String} text | dom node.. 
21804      */
21805     insertAtCursor : function(text)
21806     {
21807         
21808         if(!this.activated){
21809             return;
21810         }
21811         /*
21812         if(Roo.isIE){
21813             this.win.focus();
21814             var r = this.doc.selection.createRange();
21815             if(r){
21816                 r.collapse(true);
21817                 r.pasteHTML(text);
21818                 this.syncValue();
21819                 this.deferFocus();
21820             
21821             }
21822             return;
21823         }
21824         */
21825         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21826             this.win.focus();
21827             
21828             
21829             // from jquery ui (MIT licenced)
21830             var range, node;
21831             var win = this.win;
21832             
21833             if (win.getSelection && win.getSelection().getRangeAt) {
21834                 range = win.getSelection().getRangeAt(0);
21835                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21836                 range.insertNode(node);
21837             } else if (win.document.selection && win.document.selection.createRange) {
21838                 // no firefox support
21839                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21840                 win.document.selection.createRange().pasteHTML(txt);
21841             } else {
21842                 // no firefox support
21843                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21844                 this.execCmd('InsertHTML', txt);
21845             } 
21846             
21847             this.syncValue();
21848             
21849             this.deferFocus();
21850         }
21851     },
21852  // private
21853     mozKeyPress : function(e){
21854         if(e.ctrlKey){
21855             var c = e.getCharCode(), cmd;
21856           
21857             if(c > 0){
21858                 c = String.fromCharCode(c).toLowerCase();
21859                 switch(c){
21860                     case 'b':
21861                         cmd = 'bold';
21862                         break;
21863                     case 'i':
21864                         cmd = 'italic';
21865                         break;
21866                     
21867                     case 'u':
21868                         cmd = 'underline';
21869                         break;
21870                     
21871                     case 'v':
21872                         this.cleanUpPaste.defer(100, this);
21873                         return;
21874                         
21875                 }
21876                 if(cmd){
21877                     this.win.focus();
21878                     this.execCmd(cmd);
21879                     this.deferFocus();
21880                     e.preventDefault();
21881                 }
21882                 
21883             }
21884         }
21885     },
21886
21887     // private
21888     fixKeys : function(){ // load time branching for fastest keydown performance
21889         if(Roo.isIE){
21890             return function(e){
21891                 var k = e.getKey(), r;
21892                 if(k == e.TAB){
21893                     e.stopEvent();
21894                     r = this.doc.selection.createRange();
21895                     if(r){
21896                         r.collapse(true);
21897                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21898                         this.deferFocus();
21899                     }
21900                     return;
21901                 }
21902                 
21903                 if(k == e.ENTER){
21904                     r = this.doc.selection.createRange();
21905                     if(r){
21906                         var target = r.parentElement();
21907                         if(!target || target.tagName.toLowerCase() != 'li'){
21908                             e.stopEvent();
21909                             r.pasteHTML('<br />');
21910                             r.collapse(false);
21911                             r.select();
21912                         }
21913                     }
21914                 }
21915                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21916                     this.cleanUpPaste.defer(100, this);
21917                     return;
21918                 }
21919                 
21920                 
21921             };
21922         }else if(Roo.isOpera){
21923             return function(e){
21924                 var k = e.getKey();
21925                 if(k == e.TAB){
21926                     e.stopEvent();
21927                     this.win.focus();
21928                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21929                     this.deferFocus();
21930                 }
21931                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21932                     this.cleanUpPaste.defer(100, this);
21933                     return;
21934                 }
21935                 
21936             };
21937         }else if(Roo.isSafari){
21938             return function(e){
21939                 var k = e.getKey();
21940                 
21941                 if(k == e.TAB){
21942                     e.stopEvent();
21943                     this.execCmd('InsertText','\t');
21944                     this.deferFocus();
21945                     return;
21946                 }
21947                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21948                     this.cleanUpPaste.defer(100, this);
21949                     return;
21950                 }
21951                 
21952              };
21953         }
21954     }(),
21955     
21956     getAllAncestors: function()
21957     {
21958         var p = this.getSelectedNode();
21959         var a = [];
21960         if (!p) {
21961             a.push(p); // push blank onto stack..
21962             p = this.getParentElement();
21963         }
21964         
21965         
21966         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21967             a.push(p);
21968             p = p.parentNode;
21969         }
21970         a.push(this.doc.body);
21971         return a;
21972     },
21973     lastSel : false,
21974     lastSelNode : false,
21975     
21976     
21977     getSelection : function() 
21978     {
21979         this.assignDocWin();
21980         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21981     },
21982     
21983     getSelectedNode: function() 
21984     {
21985         // this may only work on Gecko!!!
21986         
21987         // should we cache this!!!!
21988         
21989         
21990         
21991          
21992         var range = this.createRange(this.getSelection()).cloneRange();
21993         
21994         if (Roo.isIE) {
21995             var parent = range.parentElement();
21996             while (true) {
21997                 var testRange = range.duplicate();
21998                 testRange.moveToElementText(parent);
21999                 if (testRange.inRange(range)) {
22000                     break;
22001                 }
22002                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22003                     break;
22004                 }
22005                 parent = parent.parentElement;
22006             }
22007             return parent;
22008         }
22009         
22010         // is ancestor a text element.
22011         var ac =  range.commonAncestorContainer;
22012         if (ac.nodeType == 3) {
22013             ac = ac.parentNode;
22014         }
22015         
22016         var ar = ac.childNodes;
22017          
22018         var nodes = [];
22019         var other_nodes = [];
22020         var has_other_nodes = false;
22021         for (var i=0;i<ar.length;i++) {
22022             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22023                 continue;
22024             }
22025             // fullly contained node.
22026             
22027             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22028                 nodes.push(ar[i]);
22029                 continue;
22030             }
22031             
22032             // probably selected..
22033             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22034                 other_nodes.push(ar[i]);
22035                 continue;
22036             }
22037             // outer..
22038             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22039                 continue;
22040             }
22041             
22042             
22043             has_other_nodes = true;
22044         }
22045         if (!nodes.length && other_nodes.length) {
22046             nodes= other_nodes;
22047         }
22048         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22049             return false;
22050         }
22051         
22052         return nodes[0];
22053     },
22054     createRange: function(sel)
22055     {
22056         // this has strange effects when using with 
22057         // top toolbar - not sure if it's a great idea.
22058         //this.editor.contentWindow.focus();
22059         if (typeof sel != "undefined") {
22060             try {
22061                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22062             } catch(e) {
22063                 return this.doc.createRange();
22064             }
22065         } else {
22066             return this.doc.createRange();
22067         }
22068     },
22069     getParentElement: function()
22070     {
22071         
22072         this.assignDocWin();
22073         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22074         
22075         var range = this.createRange(sel);
22076          
22077         try {
22078             var p = range.commonAncestorContainer;
22079             while (p.nodeType == 3) { // text node
22080                 p = p.parentNode;
22081             }
22082             return p;
22083         } catch (e) {
22084             return null;
22085         }
22086     
22087     },
22088     /***
22089      *
22090      * Range intersection.. the hard stuff...
22091      *  '-1' = before
22092      *  '0' = hits..
22093      *  '1' = after.
22094      *         [ -- selected range --- ]
22095      *   [fail]                        [fail]
22096      *
22097      *    basically..
22098      *      if end is before start or  hits it. fail.
22099      *      if start is after end or hits it fail.
22100      *
22101      *   if either hits (but other is outside. - then it's not 
22102      *   
22103      *    
22104      **/
22105     
22106     
22107     // @see http://www.thismuchiknow.co.uk/?p=64.
22108     rangeIntersectsNode : function(range, node)
22109     {
22110         var nodeRange = node.ownerDocument.createRange();
22111         try {
22112             nodeRange.selectNode(node);
22113         } catch (e) {
22114             nodeRange.selectNodeContents(node);
22115         }
22116     
22117         var rangeStartRange = range.cloneRange();
22118         rangeStartRange.collapse(true);
22119     
22120         var rangeEndRange = range.cloneRange();
22121         rangeEndRange.collapse(false);
22122     
22123         var nodeStartRange = nodeRange.cloneRange();
22124         nodeStartRange.collapse(true);
22125     
22126         var nodeEndRange = nodeRange.cloneRange();
22127         nodeEndRange.collapse(false);
22128     
22129         return rangeStartRange.compareBoundaryPoints(
22130                  Range.START_TO_START, nodeEndRange) == -1 &&
22131                rangeEndRange.compareBoundaryPoints(
22132                  Range.START_TO_START, nodeStartRange) == 1;
22133         
22134          
22135     },
22136     rangeCompareNode : function(range, node)
22137     {
22138         var nodeRange = node.ownerDocument.createRange();
22139         try {
22140             nodeRange.selectNode(node);
22141         } catch (e) {
22142             nodeRange.selectNodeContents(node);
22143         }
22144         
22145         
22146         range.collapse(true);
22147     
22148         nodeRange.collapse(true);
22149      
22150         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22151         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22152          
22153         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22154         
22155         var nodeIsBefore   =  ss == 1;
22156         var nodeIsAfter    = ee == -1;
22157         
22158         if (nodeIsBefore && nodeIsAfter) {
22159             return 0; // outer
22160         }
22161         if (!nodeIsBefore && nodeIsAfter) {
22162             return 1; //right trailed.
22163         }
22164         
22165         if (nodeIsBefore && !nodeIsAfter) {
22166             return 2;  // left trailed.
22167         }
22168         // fully contined.
22169         return 3;
22170     },
22171
22172     // private? - in a new class?
22173     cleanUpPaste :  function()
22174     {
22175         // cleans up the whole document..
22176         Roo.log('cleanuppaste');
22177         
22178         this.cleanUpChildren(this.doc.body);
22179         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22180         if (clean != this.doc.body.innerHTML) {
22181             this.doc.body.innerHTML = clean;
22182         }
22183         
22184     },
22185     
22186     cleanWordChars : function(input) {// change the chars to hex code
22187         var he = Roo.HtmlEditorCore;
22188         
22189         var output = input;
22190         Roo.each(he.swapCodes, function(sw) { 
22191             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22192             
22193             output = output.replace(swapper, sw[1]);
22194         });
22195         
22196         return output;
22197     },
22198     
22199     
22200     cleanUpChildren : function (n)
22201     {
22202         if (!n.childNodes.length) {
22203             return;
22204         }
22205         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22206            this.cleanUpChild(n.childNodes[i]);
22207         }
22208     },
22209     
22210     
22211         
22212     
22213     cleanUpChild : function (node)
22214     {
22215         var ed = this;
22216         //console.log(node);
22217         if (node.nodeName == "#text") {
22218             // clean up silly Windows -- stuff?
22219             return; 
22220         }
22221         if (node.nodeName == "#comment") {
22222             node.parentNode.removeChild(node);
22223             // clean up silly Windows -- stuff?
22224             return; 
22225         }
22226         var lcname = node.tagName.toLowerCase();
22227         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22228         // whitelist of tags..
22229         
22230         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22231             // remove node.
22232             node.parentNode.removeChild(node);
22233             return;
22234             
22235         }
22236         
22237         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22238         
22239         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22240         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22241         
22242         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22243         //    remove_keep_children = true;
22244         //}
22245         
22246         if (remove_keep_children) {
22247             this.cleanUpChildren(node);
22248             // inserts everything just before this node...
22249             while (node.childNodes.length) {
22250                 var cn = node.childNodes[0];
22251                 node.removeChild(cn);
22252                 node.parentNode.insertBefore(cn, node);
22253             }
22254             node.parentNode.removeChild(node);
22255             return;
22256         }
22257         
22258         if (!node.attributes || !node.attributes.length) {
22259             this.cleanUpChildren(node);
22260             return;
22261         }
22262         
22263         function cleanAttr(n,v)
22264         {
22265             
22266             if (v.match(/^\./) || v.match(/^\//)) {
22267                 return;
22268             }
22269             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22270                 return;
22271             }
22272             if (v.match(/^#/)) {
22273                 return;
22274             }
22275 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22276             node.removeAttribute(n);
22277             
22278         }
22279         
22280         var cwhite = this.cwhite;
22281         var cblack = this.cblack;
22282             
22283         function cleanStyle(n,v)
22284         {
22285             if (v.match(/expression/)) { //XSS?? should we even bother..
22286                 node.removeAttribute(n);
22287                 return;
22288             }
22289             
22290             var parts = v.split(/;/);
22291             var clean = [];
22292             
22293             Roo.each(parts, function(p) {
22294                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22295                 if (!p.length) {
22296                     return true;
22297                 }
22298                 var l = p.split(':').shift().replace(/\s+/g,'');
22299                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22300                 
22301                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22302 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22303                     //node.removeAttribute(n);
22304                     return true;
22305                 }
22306                 //Roo.log()
22307                 // only allow 'c whitelisted system attributes'
22308                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22309 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22310                     //node.removeAttribute(n);
22311                     return true;
22312                 }
22313                 
22314                 
22315                  
22316                 
22317                 clean.push(p);
22318                 return true;
22319             });
22320             if (clean.length) { 
22321                 node.setAttribute(n, clean.join(';'));
22322             } else {
22323                 node.removeAttribute(n);
22324             }
22325             
22326         }
22327         
22328         
22329         for (var i = node.attributes.length-1; i > -1 ; i--) {
22330             var a = node.attributes[i];
22331             //console.log(a);
22332             
22333             if (a.name.toLowerCase().substr(0,2)=='on')  {
22334                 node.removeAttribute(a.name);
22335                 continue;
22336             }
22337             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22338                 node.removeAttribute(a.name);
22339                 continue;
22340             }
22341             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22342                 cleanAttr(a.name,a.value); // fixme..
22343                 continue;
22344             }
22345             if (a.name == 'style') {
22346                 cleanStyle(a.name,a.value);
22347                 continue;
22348             }
22349             /// clean up MS crap..
22350             // tecnically this should be a list of valid class'es..
22351             
22352             
22353             if (a.name == 'class') {
22354                 if (a.value.match(/^Mso/)) {
22355                     node.className = '';
22356                 }
22357                 
22358                 if (a.value.match(/^body$/)) {
22359                     node.className = '';
22360                 }
22361                 continue;
22362             }
22363             
22364             // style cleanup!?
22365             // class cleanup?
22366             
22367         }
22368         
22369         
22370         this.cleanUpChildren(node);
22371         
22372         
22373     },
22374     
22375     /**
22376      * Clean up MS wordisms...
22377      */
22378     cleanWord : function(node)
22379     {
22380         
22381         
22382         if (!node) {
22383             this.cleanWord(this.doc.body);
22384             return;
22385         }
22386         if (node.nodeName == "#text") {
22387             // clean up silly Windows -- stuff?
22388             return; 
22389         }
22390         if (node.nodeName == "#comment") {
22391             node.parentNode.removeChild(node);
22392             // clean up silly Windows -- stuff?
22393             return; 
22394         }
22395         
22396         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22397             node.parentNode.removeChild(node);
22398             return;
22399         }
22400         
22401         // remove - but keep children..
22402         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22403             while (node.childNodes.length) {
22404                 var cn = node.childNodes[0];
22405                 node.removeChild(cn);
22406                 node.parentNode.insertBefore(cn, node);
22407             }
22408             node.parentNode.removeChild(node);
22409             this.iterateChildren(node, this.cleanWord);
22410             return;
22411         }
22412         // clean styles
22413         if (node.className.length) {
22414             
22415             var cn = node.className.split(/\W+/);
22416             var cna = [];
22417             Roo.each(cn, function(cls) {
22418                 if (cls.match(/Mso[a-zA-Z]+/)) {
22419                     return;
22420                 }
22421                 cna.push(cls);
22422             });
22423             node.className = cna.length ? cna.join(' ') : '';
22424             if (!cna.length) {
22425                 node.removeAttribute("class");
22426             }
22427         }
22428         
22429         if (node.hasAttribute("lang")) {
22430             node.removeAttribute("lang");
22431         }
22432         
22433         if (node.hasAttribute("style")) {
22434             
22435             var styles = node.getAttribute("style").split(";");
22436             var nstyle = [];
22437             Roo.each(styles, function(s) {
22438                 if (!s.match(/:/)) {
22439                     return;
22440                 }
22441                 var kv = s.split(":");
22442                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22443                     return;
22444                 }
22445                 // what ever is left... we allow.
22446                 nstyle.push(s);
22447             });
22448             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22449             if (!nstyle.length) {
22450                 node.removeAttribute('style');
22451             }
22452         }
22453         this.iterateChildren(node, this.cleanWord);
22454         
22455         
22456         
22457     },
22458     /**
22459      * iterateChildren of a Node, calling fn each time, using this as the scole..
22460      * @param {DomNode} node node to iterate children of.
22461      * @param {Function} fn method of this class to call on each item.
22462      */
22463     iterateChildren : function(node, fn)
22464     {
22465         if (!node.childNodes.length) {
22466                 return;
22467         }
22468         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22469            fn.call(this, node.childNodes[i])
22470         }
22471     },
22472     
22473     
22474     /**
22475      * cleanTableWidths.
22476      *
22477      * Quite often pasting from word etc.. results in tables with column and widths.
22478      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22479      *
22480      */
22481     cleanTableWidths : function(node)
22482     {
22483          
22484          
22485         if (!node) {
22486             this.cleanTableWidths(this.doc.body);
22487             return;
22488         }
22489         
22490         // ignore list...
22491         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22492             return; 
22493         }
22494         Roo.log(node.tagName);
22495         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22496             this.iterateChildren(node, this.cleanTableWidths);
22497             return;
22498         }
22499         if (node.hasAttribute('width')) {
22500             node.removeAttribute('width');
22501         }
22502         
22503          
22504         if (node.hasAttribute("style")) {
22505             // pretty basic...
22506             
22507             var styles = node.getAttribute("style").split(";");
22508             var nstyle = [];
22509             Roo.each(styles, function(s) {
22510                 if (!s.match(/:/)) {
22511                     return;
22512                 }
22513                 var kv = s.split(":");
22514                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22515                     return;
22516                 }
22517                 // what ever is left... we allow.
22518                 nstyle.push(s);
22519             });
22520             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22521             if (!nstyle.length) {
22522                 node.removeAttribute('style');
22523             }
22524         }
22525         
22526         this.iterateChildren(node, this.cleanTableWidths);
22527         
22528         
22529     },
22530     
22531     
22532     
22533     
22534     domToHTML : function(currentElement, depth, nopadtext) {
22535         
22536         depth = depth || 0;
22537         nopadtext = nopadtext || false;
22538     
22539         if (!currentElement) {
22540             return this.domToHTML(this.doc.body);
22541         }
22542         
22543         //Roo.log(currentElement);
22544         var j;
22545         var allText = false;
22546         var nodeName = currentElement.nodeName;
22547         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22548         
22549         if  (nodeName == '#text') {
22550             
22551             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22552         }
22553         
22554         
22555         var ret = '';
22556         if (nodeName != 'BODY') {
22557              
22558             var i = 0;
22559             // Prints the node tagName, such as <A>, <IMG>, etc
22560             if (tagName) {
22561                 var attr = [];
22562                 for(i = 0; i < currentElement.attributes.length;i++) {
22563                     // quoting?
22564                     var aname = currentElement.attributes.item(i).name;
22565                     if (!currentElement.attributes.item(i).value.length) {
22566                         continue;
22567                     }
22568                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22569                 }
22570                 
22571                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22572             } 
22573             else {
22574                 
22575                 // eack
22576             }
22577         } else {
22578             tagName = false;
22579         }
22580         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22581             return ret;
22582         }
22583         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22584             nopadtext = true;
22585         }
22586         
22587         
22588         // Traverse the tree
22589         i = 0;
22590         var currentElementChild = currentElement.childNodes.item(i);
22591         var allText = true;
22592         var innerHTML  = '';
22593         lastnode = '';
22594         while (currentElementChild) {
22595             // Formatting code (indent the tree so it looks nice on the screen)
22596             var nopad = nopadtext;
22597             if (lastnode == 'SPAN') {
22598                 nopad  = true;
22599             }
22600             // text
22601             if  (currentElementChild.nodeName == '#text') {
22602                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22603                 toadd = nopadtext ? toadd : toadd.trim();
22604                 if (!nopad && toadd.length > 80) {
22605                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22606                 }
22607                 innerHTML  += toadd;
22608                 
22609                 i++;
22610                 currentElementChild = currentElement.childNodes.item(i);
22611                 lastNode = '';
22612                 continue;
22613             }
22614             allText = false;
22615             
22616             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22617                 
22618             // Recursively traverse the tree structure of the child node
22619             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22620             lastnode = currentElementChild.nodeName;
22621             i++;
22622             currentElementChild=currentElement.childNodes.item(i);
22623         }
22624         
22625         ret += innerHTML;
22626         
22627         if (!allText) {
22628                 // The remaining code is mostly for formatting the tree
22629             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22630         }
22631         
22632         
22633         if (tagName) {
22634             ret+= "</"+tagName+">";
22635         }
22636         return ret;
22637         
22638     },
22639         
22640     applyBlacklists : function()
22641     {
22642         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22643         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22644         
22645         this.white = [];
22646         this.black = [];
22647         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22648             if (b.indexOf(tag) > -1) {
22649                 return;
22650             }
22651             this.white.push(tag);
22652             
22653         }, this);
22654         
22655         Roo.each(w, function(tag) {
22656             if (b.indexOf(tag) > -1) {
22657                 return;
22658             }
22659             if (this.white.indexOf(tag) > -1) {
22660                 return;
22661             }
22662             this.white.push(tag);
22663             
22664         }, this);
22665         
22666         
22667         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22668             if (w.indexOf(tag) > -1) {
22669                 return;
22670             }
22671             this.black.push(tag);
22672             
22673         }, this);
22674         
22675         Roo.each(b, function(tag) {
22676             if (w.indexOf(tag) > -1) {
22677                 return;
22678             }
22679             if (this.black.indexOf(tag) > -1) {
22680                 return;
22681             }
22682             this.black.push(tag);
22683             
22684         }, this);
22685         
22686         
22687         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22688         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22689         
22690         this.cwhite = [];
22691         this.cblack = [];
22692         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22693             if (b.indexOf(tag) > -1) {
22694                 return;
22695             }
22696             this.cwhite.push(tag);
22697             
22698         }, this);
22699         
22700         Roo.each(w, function(tag) {
22701             if (b.indexOf(tag) > -1) {
22702                 return;
22703             }
22704             if (this.cwhite.indexOf(tag) > -1) {
22705                 return;
22706             }
22707             this.cwhite.push(tag);
22708             
22709         }, this);
22710         
22711         
22712         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22713             if (w.indexOf(tag) > -1) {
22714                 return;
22715             }
22716             this.cblack.push(tag);
22717             
22718         }, this);
22719         
22720         Roo.each(b, function(tag) {
22721             if (w.indexOf(tag) > -1) {
22722                 return;
22723             }
22724             if (this.cblack.indexOf(tag) > -1) {
22725                 return;
22726             }
22727             this.cblack.push(tag);
22728             
22729         }, this);
22730     },
22731     
22732     setStylesheets : function(stylesheets)
22733     {
22734         if(typeof(stylesheets) == 'string'){
22735             Roo.get(this.iframe.contentDocument.head).createChild({
22736                 tag : 'link',
22737                 rel : 'stylesheet',
22738                 type : 'text/css',
22739                 href : stylesheets
22740             });
22741             
22742             return;
22743         }
22744         var _this = this;
22745      
22746         Roo.each(stylesheets, function(s) {
22747             if(!s.length){
22748                 return;
22749             }
22750             
22751             Roo.get(_this.iframe.contentDocument.head).createChild({
22752                 tag : 'link',
22753                 rel : 'stylesheet',
22754                 type : 'text/css',
22755                 href : s
22756             });
22757         });
22758
22759         
22760     },
22761     
22762     removeStylesheets : function()
22763     {
22764         var _this = this;
22765         
22766         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22767             s.remove();
22768         });
22769     },
22770     
22771     setStyle : function(style)
22772     {
22773         Roo.get(this.iframe.contentDocument.head).createChild({
22774             tag : 'style',
22775             type : 'text/css',
22776             html : style
22777         });
22778
22779         return;
22780     }
22781     
22782     // hide stuff that is not compatible
22783     /**
22784      * @event blur
22785      * @hide
22786      */
22787     /**
22788      * @event change
22789      * @hide
22790      */
22791     /**
22792      * @event focus
22793      * @hide
22794      */
22795     /**
22796      * @event specialkey
22797      * @hide
22798      */
22799     /**
22800      * @cfg {String} fieldClass @hide
22801      */
22802     /**
22803      * @cfg {String} focusClass @hide
22804      */
22805     /**
22806      * @cfg {String} autoCreate @hide
22807      */
22808     /**
22809      * @cfg {String} inputType @hide
22810      */
22811     /**
22812      * @cfg {String} invalidClass @hide
22813      */
22814     /**
22815      * @cfg {String} invalidText @hide
22816      */
22817     /**
22818      * @cfg {String} msgFx @hide
22819      */
22820     /**
22821      * @cfg {String} validateOnBlur @hide
22822      */
22823 });
22824
22825 Roo.HtmlEditorCore.white = [
22826         'area', 'br', 'img', 'input', 'hr', 'wbr',
22827         
22828        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22829        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22830        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22831        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22832        'table',   'ul',         'xmp', 
22833        
22834        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22835       'thead',   'tr', 
22836      
22837       'dir', 'menu', 'ol', 'ul', 'dl',
22838        
22839       'embed',  'object'
22840 ];
22841
22842
22843 Roo.HtmlEditorCore.black = [
22844     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22845         'applet', // 
22846         'base',   'basefont', 'bgsound', 'blink',  'body', 
22847         'frame',  'frameset', 'head',    'html',   'ilayer', 
22848         'iframe', 'layer',  'link',     'meta',    'object',   
22849         'script', 'style' ,'title',  'xml' // clean later..
22850 ];
22851 Roo.HtmlEditorCore.clean = [
22852     'script', 'style', 'title', 'xml'
22853 ];
22854 Roo.HtmlEditorCore.remove = [
22855     'font'
22856 ];
22857 // attributes..
22858
22859 Roo.HtmlEditorCore.ablack = [
22860     'on'
22861 ];
22862     
22863 Roo.HtmlEditorCore.aclean = [ 
22864     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22865 ];
22866
22867 // protocols..
22868 Roo.HtmlEditorCore.pwhite= [
22869         'http',  'https',  'mailto'
22870 ];
22871
22872 // white listed style attributes.
22873 Roo.HtmlEditorCore.cwhite= [
22874       //  'text-align', /// default is to allow most things..
22875       
22876          
22877 //        'font-size'//??
22878 ];
22879
22880 // black listed style attributes.
22881 Roo.HtmlEditorCore.cblack= [
22882       //  'font-size' -- this can be set by the project 
22883 ];
22884
22885
22886 Roo.HtmlEditorCore.swapCodes   =[ 
22887     [    8211, "--" ], 
22888     [    8212, "--" ], 
22889     [    8216,  "'" ],  
22890     [    8217, "'" ],  
22891     [    8220, '"' ],  
22892     [    8221, '"' ],  
22893     [    8226, "*" ],  
22894     [    8230, "..." ]
22895 ]; 
22896
22897     /*
22898  * - LGPL
22899  *
22900  * HtmlEditor
22901  * 
22902  */
22903
22904 /**
22905  * @class Roo.bootstrap.HtmlEditor
22906  * @extends Roo.bootstrap.TextArea
22907  * Bootstrap HtmlEditor class
22908
22909  * @constructor
22910  * Create a new HtmlEditor
22911  * @param {Object} config The config object
22912  */
22913
22914 Roo.bootstrap.HtmlEditor = function(config){
22915     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22916     if (!this.toolbars) {
22917         this.toolbars = [];
22918     }
22919     
22920     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22921     this.addEvents({
22922             /**
22923              * @event initialize
22924              * Fires when the editor is fully initialized (including the iframe)
22925              * @param {HtmlEditor} this
22926              */
22927             initialize: true,
22928             /**
22929              * @event activate
22930              * Fires when the editor is first receives the focus. Any insertion must wait
22931              * until after this event.
22932              * @param {HtmlEditor} this
22933              */
22934             activate: true,
22935              /**
22936              * @event beforesync
22937              * Fires before the textarea is updated with content from the editor iframe. Return false
22938              * to cancel the sync.
22939              * @param {HtmlEditor} this
22940              * @param {String} html
22941              */
22942             beforesync: true,
22943              /**
22944              * @event beforepush
22945              * Fires before the iframe editor is updated with content from the textarea. Return false
22946              * to cancel the push.
22947              * @param {HtmlEditor} this
22948              * @param {String} html
22949              */
22950             beforepush: true,
22951              /**
22952              * @event sync
22953              * Fires when the textarea is updated with content from the editor iframe.
22954              * @param {HtmlEditor} this
22955              * @param {String} html
22956              */
22957             sync: true,
22958              /**
22959              * @event push
22960              * Fires when the iframe editor is updated with content from the textarea.
22961              * @param {HtmlEditor} this
22962              * @param {String} html
22963              */
22964             push: true,
22965              /**
22966              * @event editmodechange
22967              * Fires when the editor switches edit modes
22968              * @param {HtmlEditor} this
22969              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22970              */
22971             editmodechange: true,
22972             /**
22973              * @event editorevent
22974              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22975              * @param {HtmlEditor} this
22976              */
22977             editorevent: true,
22978             /**
22979              * @event firstfocus
22980              * Fires when on first focus - needed by toolbars..
22981              * @param {HtmlEditor} this
22982              */
22983             firstfocus: true,
22984             /**
22985              * @event autosave
22986              * Auto save the htmlEditor value as a file into Events
22987              * @param {HtmlEditor} this
22988              */
22989             autosave: true,
22990             /**
22991              * @event savedpreview
22992              * preview the saved version of htmlEditor
22993              * @param {HtmlEditor} this
22994              */
22995             savedpreview: true
22996         });
22997 };
22998
22999
23000 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23001     
23002     
23003       /**
23004      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23005      */
23006     toolbars : false,
23007     
23008      /**
23009     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23010     */
23011     btns : [],
23012    
23013      /**
23014      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23015      *                        Roo.resizable.
23016      */
23017     resizable : false,
23018      /**
23019      * @cfg {Number} height (in pixels)
23020      */   
23021     height: 300,
23022    /**
23023      * @cfg {Number} width (in pixels)
23024      */   
23025     width: false,
23026     
23027     /**
23028      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23029      * 
23030      */
23031     stylesheets: false,
23032     
23033     // id of frame..
23034     frameId: false,
23035     
23036     // private properties
23037     validationEvent : false,
23038     deferHeight: true,
23039     initialized : false,
23040     activated : false,
23041     
23042     onFocus : Roo.emptyFn,
23043     iframePad:3,
23044     hideMode:'offsets',
23045     
23046     tbContainer : false,
23047     
23048     bodyCls : '',
23049     
23050     toolbarContainer :function() {
23051         return this.wrap.select('.x-html-editor-tb',true).first();
23052     },
23053
23054     /**
23055      * Protected method that will not generally be called directly. It
23056      * is called when the editor creates its toolbar. Override this method if you need to
23057      * add custom toolbar buttons.
23058      * @param {HtmlEditor} editor
23059      */
23060     createToolbar : function(){
23061         Roo.log('renewing');
23062         Roo.log("create toolbars");
23063         
23064         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23065         this.toolbars[0].render(this.toolbarContainer());
23066         
23067         return;
23068         
23069 //        if (!editor.toolbars || !editor.toolbars.length) {
23070 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23071 //        }
23072 //        
23073 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23074 //            editor.toolbars[i] = Roo.factory(
23075 //                    typeof(editor.toolbars[i]) == 'string' ?
23076 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23077 //                Roo.bootstrap.HtmlEditor);
23078 //            editor.toolbars[i].init(editor);
23079 //        }
23080     },
23081
23082      
23083     // private
23084     onRender : function(ct, position)
23085     {
23086        // Roo.log("Call onRender: " + this.xtype);
23087         var _t = this;
23088         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23089       
23090         this.wrap = this.inputEl().wrap({
23091             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23092         });
23093         
23094         this.editorcore.onRender(ct, position);
23095          
23096         if (this.resizable) {
23097             this.resizeEl = new Roo.Resizable(this.wrap, {
23098                 pinned : true,
23099                 wrap: true,
23100                 dynamic : true,
23101                 minHeight : this.height,
23102                 height: this.height,
23103                 handles : this.resizable,
23104                 width: this.width,
23105                 listeners : {
23106                     resize : function(r, w, h) {
23107                         _t.onResize(w,h); // -something
23108                     }
23109                 }
23110             });
23111             
23112         }
23113         this.createToolbar(this);
23114        
23115         
23116         if(!this.width && this.resizable){
23117             this.setSize(this.wrap.getSize());
23118         }
23119         if (this.resizeEl) {
23120             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23121             // should trigger onReize..
23122         }
23123         
23124     },
23125
23126     // private
23127     onResize : function(w, h)
23128     {
23129         Roo.log('resize: ' +w + ',' + h );
23130         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23131         var ew = false;
23132         var eh = false;
23133         
23134         if(this.inputEl() ){
23135             if(typeof w == 'number'){
23136                 var aw = w - this.wrap.getFrameWidth('lr');
23137                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23138                 ew = aw;
23139             }
23140             if(typeof h == 'number'){
23141                  var tbh = -11;  // fixme it needs to tool bar size!
23142                 for (var i =0; i < this.toolbars.length;i++) {
23143                     // fixme - ask toolbars for heights?
23144                     tbh += this.toolbars[i].el.getHeight();
23145                     //if (this.toolbars[i].footer) {
23146                     //    tbh += this.toolbars[i].footer.el.getHeight();
23147                     //}
23148                 }
23149               
23150                 
23151                 
23152                 
23153                 
23154                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23155                 ah -= 5; // knock a few pixes off for look..
23156                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23157                 var eh = ah;
23158             }
23159         }
23160         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23161         this.editorcore.onResize(ew,eh);
23162         
23163     },
23164
23165     /**
23166      * Toggles the editor between standard and source edit mode.
23167      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23168      */
23169     toggleSourceEdit : function(sourceEditMode)
23170     {
23171         this.editorcore.toggleSourceEdit(sourceEditMode);
23172         
23173         if(this.editorcore.sourceEditMode){
23174             Roo.log('editor - showing textarea');
23175             
23176 //            Roo.log('in');
23177 //            Roo.log(this.syncValue());
23178             this.syncValue();
23179             this.inputEl().removeClass(['hide', 'x-hidden']);
23180             this.inputEl().dom.removeAttribute('tabIndex');
23181             this.inputEl().focus();
23182         }else{
23183             Roo.log('editor - hiding textarea');
23184 //            Roo.log('out')
23185 //            Roo.log(this.pushValue()); 
23186             this.pushValue();
23187             
23188             this.inputEl().addClass(['hide', 'x-hidden']);
23189             this.inputEl().dom.setAttribute('tabIndex', -1);
23190             //this.deferFocus();
23191         }
23192          
23193         if(this.resizable){
23194             this.setSize(this.wrap.getSize());
23195         }
23196         
23197         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23198     },
23199  
23200     // private (for BoxComponent)
23201     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23202
23203     // private (for BoxComponent)
23204     getResizeEl : function(){
23205         return this.wrap;
23206     },
23207
23208     // private (for BoxComponent)
23209     getPositionEl : function(){
23210         return this.wrap;
23211     },
23212
23213     // private
23214     initEvents : function(){
23215         this.originalValue = this.getValue();
23216     },
23217
23218 //    /**
23219 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23220 //     * @method
23221 //     */
23222 //    markInvalid : Roo.emptyFn,
23223 //    /**
23224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23225 //     * @method
23226 //     */
23227 //    clearInvalid : Roo.emptyFn,
23228
23229     setValue : function(v){
23230         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23231         this.editorcore.pushValue();
23232     },
23233
23234      
23235     // private
23236     deferFocus : function(){
23237         this.focus.defer(10, this);
23238     },
23239
23240     // doc'ed in Field
23241     focus : function(){
23242         this.editorcore.focus();
23243         
23244     },
23245       
23246
23247     // private
23248     onDestroy : function(){
23249         
23250         
23251         
23252         if(this.rendered){
23253             
23254             for (var i =0; i < this.toolbars.length;i++) {
23255                 // fixme - ask toolbars for heights?
23256                 this.toolbars[i].onDestroy();
23257             }
23258             
23259             this.wrap.dom.innerHTML = '';
23260             this.wrap.remove();
23261         }
23262     },
23263
23264     // private
23265     onFirstFocus : function(){
23266         //Roo.log("onFirstFocus");
23267         this.editorcore.onFirstFocus();
23268          for (var i =0; i < this.toolbars.length;i++) {
23269             this.toolbars[i].onFirstFocus();
23270         }
23271         
23272     },
23273     
23274     // private
23275     syncValue : function()
23276     {   
23277         this.editorcore.syncValue();
23278     },
23279     
23280     pushValue : function()
23281     {   
23282         this.editorcore.pushValue();
23283     }
23284      
23285     
23286     // hide stuff that is not compatible
23287     /**
23288      * @event blur
23289      * @hide
23290      */
23291     /**
23292      * @event change
23293      * @hide
23294      */
23295     /**
23296      * @event focus
23297      * @hide
23298      */
23299     /**
23300      * @event specialkey
23301      * @hide
23302      */
23303     /**
23304      * @cfg {String} fieldClass @hide
23305      */
23306     /**
23307      * @cfg {String} focusClass @hide
23308      */
23309     /**
23310      * @cfg {String} autoCreate @hide
23311      */
23312     /**
23313      * @cfg {String} inputType @hide
23314      */
23315     /**
23316      * @cfg {String} invalidClass @hide
23317      */
23318     /**
23319      * @cfg {String} invalidText @hide
23320      */
23321     /**
23322      * @cfg {String} msgFx @hide
23323      */
23324     /**
23325      * @cfg {String} validateOnBlur @hide
23326      */
23327 });
23328  
23329     
23330    
23331    
23332    
23333       
23334 Roo.namespace('Roo.bootstrap.htmleditor');
23335 /**
23336  * @class Roo.bootstrap.HtmlEditorToolbar1
23337  * Basic Toolbar
23338  * 
23339  * Usage:
23340  *
23341  new Roo.bootstrap.HtmlEditor({
23342     ....
23343     toolbars : [
23344         new Roo.bootstrap.HtmlEditorToolbar1({
23345             disable : { fonts: 1 , format: 1, ..., ... , ...],
23346             btns : [ .... ]
23347         })
23348     }
23349      
23350  * 
23351  * @cfg {Object} disable List of elements to disable..
23352  * @cfg {Array} btns List of additional buttons.
23353  * 
23354  * 
23355  * NEEDS Extra CSS? 
23356  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23357  */
23358  
23359 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23360 {
23361     
23362     Roo.apply(this, config);
23363     
23364     // default disabled, based on 'good practice'..
23365     this.disable = this.disable || {};
23366     Roo.applyIf(this.disable, {
23367         fontSize : true,
23368         colors : true,
23369         specialElements : true
23370     });
23371     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23372     
23373     this.editor = config.editor;
23374     this.editorcore = config.editor.editorcore;
23375     
23376     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23377     
23378     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23379     // dont call parent... till later.
23380 }
23381 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23382      
23383     bar : true,
23384     
23385     editor : false,
23386     editorcore : false,
23387     
23388     
23389     formats : [
23390         "p" ,  
23391         "h1","h2","h3","h4","h5","h6", 
23392         "pre", "code", 
23393         "abbr", "acronym", "address", "cite", "samp", "var",
23394         'div','span'
23395     ],
23396     
23397     onRender : function(ct, position)
23398     {
23399        // Roo.log("Call onRender: " + this.xtype);
23400         
23401        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23402        Roo.log(this.el);
23403        this.el.dom.style.marginBottom = '0';
23404        var _this = this;
23405        var editorcore = this.editorcore;
23406        var editor= this.editor;
23407        
23408        var children = [];
23409        var btn = function(id,cmd , toggle, handler, html){
23410        
23411             var  event = toggle ? 'toggle' : 'click';
23412        
23413             var a = {
23414                 size : 'sm',
23415                 xtype: 'Button',
23416                 xns: Roo.bootstrap,
23417                 glyphicon : id,
23418                 cmd : id || cmd,
23419                 enableToggle:toggle !== false,
23420                 html : html || '',
23421                 pressed : toggle ? false : null,
23422                 listeners : {}
23423             };
23424             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23425                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23426             };
23427             children.push(a);
23428             return a;
23429        }
23430        
23431     //    var cb_box = function...
23432         
23433         var style = {
23434                 xtype: 'Button',
23435                 size : 'sm',
23436                 xns: Roo.bootstrap,
23437                 glyphicon : 'font',
23438                 //html : 'submit'
23439                 menu : {
23440                     xtype: 'Menu',
23441                     xns: Roo.bootstrap,
23442                     items:  []
23443                 }
23444         };
23445         Roo.each(this.formats, function(f) {
23446             style.menu.items.push({
23447                 xtype :'MenuItem',
23448                 xns: Roo.bootstrap,
23449                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23450                 tagname : f,
23451                 listeners : {
23452                     click : function()
23453                     {
23454                         editorcore.insertTag(this.tagname);
23455                         editor.focus();
23456                     }
23457                 }
23458                 
23459             });
23460         });
23461         children.push(style);   
23462         
23463         btn('bold',false,true);
23464         btn('italic',false,true);
23465         btn('align-left', 'justifyleft',true);
23466         btn('align-center', 'justifycenter',true);
23467         btn('align-right' , 'justifyright',true);
23468         btn('link', false, false, function(btn) {
23469             //Roo.log("create link?");
23470             var url = prompt(this.createLinkText, this.defaultLinkValue);
23471             if(url && url != 'http:/'+'/'){
23472                 this.editorcore.relayCmd('createlink', url);
23473             }
23474         }),
23475         btn('list','insertunorderedlist',true);
23476         btn('pencil', false,true, function(btn){
23477                 Roo.log(this);
23478                 this.toggleSourceEdit(btn.pressed);
23479         });
23480         
23481         if (this.editor.btns.length > 0) {
23482             for (var i = 0; i<this.editor.btns.length; i++) {
23483                 children.push(this.editor.btns[i]);
23484             }
23485         }
23486         
23487         /*
23488         var cog = {
23489                 xtype: 'Button',
23490                 size : 'sm',
23491                 xns: Roo.bootstrap,
23492                 glyphicon : 'cog',
23493                 //html : 'submit'
23494                 menu : {
23495                     xtype: 'Menu',
23496                     xns: Roo.bootstrap,
23497                     items:  []
23498                 }
23499         };
23500         
23501         cog.menu.items.push({
23502             xtype :'MenuItem',
23503             xns: Roo.bootstrap,
23504             html : Clean styles,
23505             tagname : f,
23506             listeners : {
23507                 click : function()
23508                 {
23509                     editorcore.insertTag(this.tagname);
23510                     editor.focus();
23511                 }
23512             }
23513             
23514         });
23515        */
23516         
23517          
23518        this.xtype = 'NavSimplebar';
23519         
23520         for(var i=0;i< children.length;i++) {
23521             
23522             this.buttons.add(this.addxtypeChild(children[i]));
23523             
23524         }
23525         
23526         editor.on('editorevent', this.updateToolbar, this);
23527     },
23528     onBtnClick : function(id)
23529     {
23530        this.editorcore.relayCmd(id);
23531        this.editorcore.focus();
23532     },
23533     
23534     /**
23535      * Protected method that will not generally be called directly. It triggers
23536      * a toolbar update by reading the markup state of the current selection in the editor.
23537      */
23538     updateToolbar: function(){
23539
23540         if(!this.editorcore.activated){
23541             this.editor.onFirstFocus(); // is this neeed?
23542             return;
23543         }
23544
23545         var btns = this.buttons; 
23546         var doc = this.editorcore.doc;
23547         btns.get('bold').setActive(doc.queryCommandState('bold'));
23548         btns.get('italic').setActive(doc.queryCommandState('italic'));
23549         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23550         
23551         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23552         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23553         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23554         
23555         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23556         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23557          /*
23558         
23559         var ans = this.editorcore.getAllAncestors();
23560         if (this.formatCombo) {
23561             
23562             
23563             var store = this.formatCombo.store;
23564             this.formatCombo.setValue("");
23565             for (var i =0; i < ans.length;i++) {
23566                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23567                     // select it..
23568                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23569                     break;
23570                 }
23571             }
23572         }
23573         
23574         
23575         
23576         // hides menus... - so this cant be on a menu...
23577         Roo.bootstrap.MenuMgr.hideAll();
23578         */
23579         Roo.bootstrap.MenuMgr.hideAll();
23580         //this.editorsyncValue();
23581     },
23582     onFirstFocus: function() {
23583         this.buttons.each(function(item){
23584            item.enable();
23585         });
23586     },
23587     toggleSourceEdit : function(sourceEditMode){
23588         
23589           
23590         if(sourceEditMode){
23591             Roo.log("disabling buttons");
23592            this.buttons.each( function(item){
23593                 if(item.cmd != 'pencil'){
23594                     item.disable();
23595                 }
23596             });
23597           
23598         }else{
23599             Roo.log("enabling buttons");
23600             if(this.editorcore.initialized){
23601                 this.buttons.each( function(item){
23602                     item.enable();
23603                 });
23604             }
23605             
23606         }
23607         Roo.log("calling toggole on editor");
23608         // tell the editor that it's been pressed..
23609         this.editor.toggleSourceEdit(sourceEditMode);
23610        
23611     }
23612 });
23613
23614
23615
23616
23617
23618 /**
23619  * @class Roo.bootstrap.Table.AbstractSelectionModel
23620  * @extends Roo.util.Observable
23621  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23622  * implemented by descendant classes.  This class should not be directly instantiated.
23623  * @constructor
23624  */
23625 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23626     this.locked = false;
23627     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23628 };
23629
23630
23631 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23632     /** @ignore Called by the grid automatically. Do not call directly. */
23633     init : function(grid){
23634         this.grid = grid;
23635         this.initEvents();
23636     },
23637
23638     /**
23639      * Locks the selections.
23640      */
23641     lock : function(){
23642         this.locked = true;
23643     },
23644
23645     /**
23646      * Unlocks the selections.
23647      */
23648     unlock : function(){
23649         this.locked = false;
23650     },
23651
23652     /**
23653      * Returns true if the selections are locked.
23654      * @return {Boolean}
23655      */
23656     isLocked : function(){
23657         return this.locked;
23658     }
23659 });
23660 /**
23661  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23662  * @class Roo.bootstrap.Table.RowSelectionModel
23663  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23664  * It supports multiple selections and keyboard selection/navigation. 
23665  * @constructor
23666  * @param {Object} config
23667  */
23668
23669 Roo.bootstrap.Table.RowSelectionModel = function(config){
23670     Roo.apply(this, config);
23671     this.selections = new Roo.util.MixedCollection(false, function(o){
23672         return o.id;
23673     });
23674
23675     this.last = false;
23676     this.lastActive = false;
23677
23678     this.addEvents({
23679         /**
23680              * @event selectionchange
23681              * Fires when the selection changes
23682              * @param {SelectionModel} this
23683              */
23684             "selectionchange" : true,
23685         /**
23686              * @event afterselectionchange
23687              * Fires after the selection changes (eg. by key press or clicking)
23688              * @param {SelectionModel} this
23689              */
23690             "afterselectionchange" : true,
23691         /**
23692              * @event beforerowselect
23693              * Fires when a row is selected being selected, return false to cancel.
23694              * @param {SelectionModel} this
23695              * @param {Number} rowIndex The selected index
23696              * @param {Boolean} keepExisting False if other selections will be cleared
23697              */
23698             "beforerowselect" : true,
23699         /**
23700              * @event rowselect
23701              * Fires when a row is selected.
23702              * @param {SelectionModel} this
23703              * @param {Number} rowIndex The selected index
23704              * @param {Roo.data.Record} r The record
23705              */
23706             "rowselect" : true,
23707         /**
23708              * @event rowdeselect
23709              * Fires when a row is deselected.
23710              * @param {SelectionModel} this
23711              * @param {Number} rowIndex The selected index
23712              */
23713         "rowdeselect" : true
23714     });
23715     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23716     this.locked = false;
23717  };
23718
23719 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23720     /**
23721      * @cfg {Boolean} singleSelect
23722      * True to allow selection of only one row at a time (defaults to false)
23723      */
23724     singleSelect : false,
23725
23726     // private
23727     initEvents : function()
23728     {
23729
23730         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23731         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23732         //}else{ // allow click to work like normal
23733          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23734         //}
23735         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23736         this.grid.on("rowclick", this.handleMouseDown, this);
23737         
23738         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23739             "up" : function(e){
23740                 if(!e.shiftKey){
23741                     this.selectPrevious(e.shiftKey);
23742                 }else if(this.last !== false && this.lastActive !== false){
23743                     var last = this.last;
23744                     this.selectRange(this.last,  this.lastActive-1);
23745                     this.grid.getView().focusRow(this.lastActive);
23746                     if(last !== false){
23747                         this.last = last;
23748                     }
23749                 }else{
23750                     this.selectFirstRow();
23751                 }
23752                 this.fireEvent("afterselectionchange", this);
23753             },
23754             "down" : function(e){
23755                 if(!e.shiftKey){
23756                     this.selectNext(e.shiftKey);
23757                 }else if(this.last !== false && this.lastActive !== false){
23758                     var last = this.last;
23759                     this.selectRange(this.last,  this.lastActive+1);
23760                     this.grid.getView().focusRow(this.lastActive);
23761                     if(last !== false){
23762                         this.last = last;
23763                     }
23764                 }else{
23765                     this.selectFirstRow();
23766                 }
23767                 this.fireEvent("afterselectionchange", this);
23768             },
23769             scope: this
23770         });
23771         this.grid.store.on('load', function(){
23772             this.selections.clear();
23773         },this);
23774         /*
23775         var view = this.grid.view;
23776         view.on("refresh", this.onRefresh, this);
23777         view.on("rowupdated", this.onRowUpdated, this);
23778         view.on("rowremoved", this.onRemove, this);
23779         */
23780     },
23781
23782     // private
23783     onRefresh : function()
23784     {
23785         var ds = this.grid.store, i, v = this.grid.view;
23786         var s = this.selections;
23787         s.each(function(r){
23788             if((i = ds.indexOfId(r.id)) != -1){
23789                 v.onRowSelect(i);
23790             }else{
23791                 s.remove(r);
23792             }
23793         });
23794     },
23795
23796     // private
23797     onRemove : function(v, index, r){
23798         this.selections.remove(r);
23799     },
23800
23801     // private
23802     onRowUpdated : function(v, index, r){
23803         if(this.isSelected(r)){
23804             v.onRowSelect(index);
23805         }
23806     },
23807
23808     /**
23809      * Select records.
23810      * @param {Array} records The records to select
23811      * @param {Boolean} keepExisting (optional) True to keep existing selections
23812      */
23813     selectRecords : function(records, keepExisting)
23814     {
23815         if(!keepExisting){
23816             this.clearSelections();
23817         }
23818             var ds = this.grid.store;
23819         for(var i = 0, len = records.length; i < len; i++){
23820             this.selectRow(ds.indexOf(records[i]), true);
23821         }
23822     },
23823
23824     /**
23825      * Gets the number of selected rows.
23826      * @return {Number}
23827      */
23828     getCount : function(){
23829         return this.selections.length;
23830     },
23831
23832     /**
23833      * Selects the first row in the grid.
23834      */
23835     selectFirstRow : function(){
23836         this.selectRow(0);
23837     },
23838
23839     /**
23840      * Select the last row.
23841      * @param {Boolean} keepExisting (optional) True to keep existing selections
23842      */
23843     selectLastRow : function(keepExisting){
23844         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23845         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23846     },
23847
23848     /**
23849      * Selects the row immediately following the last selected row.
23850      * @param {Boolean} keepExisting (optional) True to keep existing selections
23851      */
23852     selectNext : function(keepExisting)
23853     {
23854             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23855             this.selectRow(this.last+1, keepExisting);
23856             this.grid.getView().focusRow(this.last);
23857         }
23858     },
23859
23860     /**
23861      * Selects the row that precedes the last selected row.
23862      * @param {Boolean} keepExisting (optional) True to keep existing selections
23863      */
23864     selectPrevious : function(keepExisting){
23865         if(this.last){
23866             this.selectRow(this.last-1, keepExisting);
23867             this.grid.getView().focusRow(this.last);
23868         }
23869     },
23870
23871     /**
23872      * Returns the selected records
23873      * @return {Array} Array of selected records
23874      */
23875     getSelections : function(){
23876         return [].concat(this.selections.items);
23877     },
23878
23879     /**
23880      * Returns the first selected record.
23881      * @return {Record}
23882      */
23883     getSelected : function(){
23884         return this.selections.itemAt(0);
23885     },
23886
23887
23888     /**
23889      * Clears all selections.
23890      */
23891     clearSelections : function(fast)
23892     {
23893         if(this.locked) {
23894             return;
23895         }
23896         if(fast !== true){
23897                 var ds = this.grid.store;
23898             var s = this.selections;
23899             s.each(function(r){
23900                 this.deselectRow(ds.indexOfId(r.id));
23901             }, this);
23902             s.clear();
23903         }else{
23904             this.selections.clear();
23905         }
23906         this.last = false;
23907     },
23908
23909
23910     /**
23911      * Selects all rows.
23912      */
23913     selectAll : function(){
23914         if(this.locked) {
23915             return;
23916         }
23917         this.selections.clear();
23918         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23919             this.selectRow(i, true);
23920         }
23921     },
23922
23923     /**
23924      * Returns True if there is a selection.
23925      * @return {Boolean}
23926      */
23927     hasSelection : function(){
23928         return this.selections.length > 0;
23929     },
23930
23931     /**
23932      * Returns True if the specified row is selected.
23933      * @param {Number/Record} record The record or index of the record to check
23934      * @return {Boolean}
23935      */
23936     isSelected : function(index){
23937             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23938         return (r && this.selections.key(r.id) ? true : false);
23939     },
23940
23941     /**
23942      * Returns True if the specified record id is selected.
23943      * @param {String} id The id of record to check
23944      * @return {Boolean}
23945      */
23946     isIdSelected : function(id){
23947         return (this.selections.key(id) ? true : false);
23948     },
23949
23950
23951     // private
23952     handleMouseDBClick : function(e, t){
23953         
23954     },
23955     // private
23956     handleMouseDown : function(e, t)
23957     {
23958             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23959         if(this.isLocked() || rowIndex < 0 ){
23960             return;
23961         };
23962         if(e.shiftKey && this.last !== false){
23963             var last = this.last;
23964             this.selectRange(last, rowIndex, e.ctrlKey);
23965             this.last = last; // reset the last
23966             t.focus();
23967     
23968         }else{
23969             var isSelected = this.isSelected(rowIndex);
23970             //Roo.log("select row:" + rowIndex);
23971             if(isSelected){
23972                 this.deselectRow(rowIndex);
23973             } else {
23974                         this.selectRow(rowIndex, true);
23975             }
23976     
23977             /*
23978                 if(e.button !== 0 && isSelected){
23979                 alert('rowIndex 2: ' + rowIndex);
23980                     view.focusRow(rowIndex);
23981                 }else if(e.ctrlKey && isSelected){
23982                     this.deselectRow(rowIndex);
23983                 }else if(!isSelected){
23984                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23985                     view.focusRow(rowIndex);
23986                 }
23987             */
23988         }
23989         this.fireEvent("afterselectionchange", this);
23990     },
23991     // private
23992     handleDragableRowClick :  function(grid, rowIndex, e) 
23993     {
23994         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23995             this.selectRow(rowIndex, false);
23996             grid.view.focusRow(rowIndex);
23997              this.fireEvent("afterselectionchange", this);
23998         }
23999     },
24000     
24001     /**
24002      * Selects multiple rows.
24003      * @param {Array} rows Array of the indexes of the row to select
24004      * @param {Boolean} keepExisting (optional) True to keep existing selections
24005      */
24006     selectRows : function(rows, keepExisting){
24007         if(!keepExisting){
24008             this.clearSelections();
24009         }
24010         for(var i = 0, len = rows.length; i < len; i++){
24011             this.selectRow(rows[i], true);
24012         }
24013     },
24014
24015     /**
24016      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24017      * @param {Number} startRow The index of the first row in the range
24018      * @param {Number} endRow The index of the last row in the range
24019      * @param {Boolean} keepExisting (optional) True to retain existing selections
24020      */
24021     selectRange : function(startRow, endRow, keepExisting){
24022         if(this.locked) {
24023             return;
24024         }
24025         if(!keepExisting){
24026             this.clearSelections();
24027         }
24028         if(startRow <= endRow){
24029             for(var i = startRow; i <= endRow; i++){
24030                 this.selectRow(i, true);
24031             }
24032         }else{
24033             for(var i = startRow; i >= endRow; i--){
24034                 this.selectRow(i, true);
24035             }
24036         }
24037     },
24038
24039     /**
24040      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24041      * @param {Number} startRow The index of the first row in the range
24042      * @param {Number} endRow The index of the last row in the range
24043      */
24044     deselectRange : function(startRow, endRow, preventViewNotify){
24045         if(this.locked) {
24046             return;
24047         }
24048         for(var i = startRow; i <= endRow; i++){
24049             this.deselectRow(i, preventViewNotify);
24050         }
24051     },
24052
24053     /**
24054      * Selects a row.
24055      * @param {Number} row The index of the row to select
24056      * @param {Boolean} keepExisting (optional) True to keep existing selections
24057      */
24058     selectRow : function(index, keepExisting, preventViewNotify)
24059     {
24060             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24061             return;
24062         }
24063         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24064             if(!keepExisting || this.singleSelect){
24065                 this.clearSelections();
24066             }
24067             
24068             var r = this.grid.store.getAt(index);
24069             //console.log('selectRow - record id :' + r.id);
24070             
24071             this.selections.add(r);
24072             this.last = this.lastActive = index;
24073             if(!preventViewNotify){
24074                 var proxy = new Roo.Element(
24075                                 this.grid.getRowDom(index)
24076                 );
24077                 proxy.addClass('bg-info info');
24078             }
24079             this.fireEvent("rowselect", this, index, r);
24080             this.fireEvent("selectionchange", this);
24081         }
24082     },
24083
24084     /**
24085      * Deselects a row.
24086      * @param {Number} row The index of the row to deselect
24087      */
24088     deselectRow : function(index, preventViewNotify)
24089     {
24090         if(this.locked) {
24091             return;
24092         }
24093         if(this.last == index){
24094             this.last = false;
24095         }
24096         if(this.lastActive == index){
24097             this.lastActive = false;
24098         }
24099         
24100         var r = this.grid.store.getAt(index);
24101         if (!r) {
24102             return;
24103         }
24104         
24105         this.selections.remove(r);
24106         //.console.log('deselectRow - record id :' + r.id);
24107         if(!preventViewNotify){
24108         
24109             var proxy = new Roo.Element(
24110                 this.grid.getRowDom(index)
24111             );
24112             proxy.removeClass('bg-info info');
24113         }
24114         this.fireEvent("rowdeselect", this, index);
24115         this.fireEvent("selectionchange", this);
24116     },
24117
24118     // private
24119     restoreLast : function(){
24120         if(this._last){
24121             this.last = this._last;
24122         }
24123     },
24124
24125     // private
24126     acceptsNav : function(row, col, cm){
24127         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24128     },
24129
24130     // private
24131     onEditorKey : function(field, e){
24132         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24133         if(k == e.TAB){
24134             e.stopEvent();
24135             ed.completeEdit();
24136             if(e.shiftKey){
24137                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24138             }else{
24139                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24140             }
24141         }else if(k == e.ENTER && !e.ctrlKey){
24142             e.stopEvent();
24143             ed.completeEdit();
24144             if(e.shiftKey){
24145                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24146             }else{
24147                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24148             }
24149         }else if(k == e.ESC){
24150             ed.cancelEdit();
24151         }
24152         if(newCell){
24153             g.startEditing(newCell[0], newCell[1]);
24154         }
24155     }
24156 });
24157 /*
24158  * Based on:
24159  * Ext JS Library 1.1.1
24160  * Copyright(c) 2006-2007, Ext JS, LLC.
24161  *
24162  * Originally Released Under LGPL - original licence link has changed is not relivant.
24163  *
24164  * Fork - LGPL
24165  * <script type="text/javascript">
24166  */
24167  
24168 /**
24169  * @class Roo.bootstrap.PagingToolbar
24170  * @extends Roo.bootstrap.NavSimplebar
24171  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24172  * @constructor
24173  * Create a new PagingToolbar
24174  * @param {Object} config The config object
24175  * @param {Roo.data.Store} store
24176  */
24177 Roo.bootstrap.PagingToolbar = function(config)
24178 {
24179     // old args format still supported... - xtype is prefered..
24180         // created from xtype...
24181     
24182     this.ds = config.dataSource;
24183     
24184     if (config.store && !this.ds) {
24185         this.store= Roo.factory(config.store, Roo.data);
24186         this.ds = this.store;
24187         this.ds.xmodule = this.xmodule || false;
24188     }
24189     
24190     this.toolbarItems = [];
24191     if (config.items) {
24192         this.toolbarItems = config.items;
24193     }
24194     
24195     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24196     
24197     this.cursor = 0;
24198     
24199     if (this.ds) { 
24200         this.bind(this.ds);
24201     }
24202     
24203     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24204     
24205 };
24206
24207 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24208     /**
24209      * @cfg {Roo.data.Store} dataSource
24210      * The underlying data store providing the paged data
24211      */
24212     /**
24213      * @cfg {String/HTMLElement/Element} container
24214      * container The id or element that will contain the toolbar
24215      */
24216     /**
24217      * @cfg {Boolean} displayInfo
24218      * True to display the displayMsg (defaults to false)
24219      */
24220     /**
24221      * @cfg {Number} pageSize
24222      * The number of records to display per page (defaults to 20)
24223      */
24224     pageSize: 20,
24225     /**
24226      * @cfg {String} displayMsg
24227      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24228      */
24229     displayMsg : 'Displaying {0} - {1} of {2}',
24230     /**
24231      * @cfg {String} emptyMsg
24232      * The message to display when no records are found (defaults to "No data to display")
24233      */
24234     emptyMsg : 'No data to display',
24235     /**
24236      * Customizable piece of the default paging text (defaults to "Page")
24237      * @type String
24238      */
24239     beforePageText : "Page",
24240     /**
24241      * Customizable piece of the default paging text (defaults to "of %0")
24242      * @type String
24243      */
24244     afterPageText : "of {0}",
24245     /**
24246      * Customizable piece of the default paging text (defaults to "First Page")
24247      * @type String
24248      */
24249     firstText : "First Page",
24250     /**
24251      * Customizable piece of the default paging text (defaults to "Previous Page")
24252      * @type String
24253      */
24254     prevText : "Previous Page",
24255     /**
24256      * Customizable piece of the default paging text (defaults to "Next Page")
24257      * @type String
24258      */
24259     nextText : "Next Page",
24260     /**
24261      * Customizable piece of the default paging text (defaults to "Last Page")
24262      * @type String
24263      */
24264     lastText : "Last Page",
24265     /**
24266      * Customizable piece of the default paging text (defaults to "Refresh")
24267      * @type String
24268      */
24269     refreshText : "Refresh",
24270
24271     buttons : false,
24272     // private
24273     onRender : function(ct, position) 
24274     {
24275         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24276         this.navgroup.parentId = this.id;
24277         this.navgroup.onRender(this.el, null);
24278         // add the buttons to the navgroup
24279         
24280         if(this.displayInfo){
24281             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24282             this.displayEl = this.el.select('.x-paging-info', true).first();
24283 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24284 //            this.displayEl = navel.el.select('span',true).first();
24285         }
24286         
24287         var _this = this;
24288         
24289         if(this.buttons){
24290             Roo.each(_this.buttons, function(e){ // this might need to use render????
24291                Roo.factory(e).onRender(_this.el, null);
24292             });
24293         }
24294             
24295         Roo.each(_this.toolbarItems, function(e) {
24296             _this.navgroup.addItem(e);
24297         });
24298         
24299         
24300         this.first = this.navgroup.addItem({
24301             tooltip: this.firstText,
24302             cls: "prev",
24303             icon : 'fa fa-backward',
24304             disabled: true,
24305             preventDefault: true,
24306             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24307         });
24308         
24309         this.prev =  this.navgroup.addItem({
24310             tooltip: this.prevText,
24311             cls: "prev",
24312             icon : 'fa fa-step-backward',
24313             disabled: true,
24314             preventDefault: true,
24315             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24316         });
24317     //this.addSeparator();
24318         
24319         
24320         var field = this.navgroup.addItem( {
24321             tagtype : 'span',
24322             cls : 'x-paging-position',
24323             
24324             html : this.beforePageText  +
24325                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24326                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24327          } ); //?? escaped?
24328         
24329         this.field = field.el.select('input', true).first();
24330         this.field.on("keydown", this.onPagingKeydown, this);
24331         this.field.on("focus", function(){this.dom.select();});
24332     
24333     
24334         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24335         //this.field.setHeight(18);
24336         //this.addSeparator();
24337         this.next = this.navgroup.addItem({
24338             tooltip: this.nextText,
24339             cls: "next",
24340             html : ' <i class="fa fa-step-forward">',
24341             disabled: true,
24342             preventDefault: true,
24343             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24344         });
24345         this.last = this.navgroup.addItem({
24346             tooltip: this.lastText,
24347             icon : 'fa fa-forward',
24348             cls: "next",
24349             disabled: true,
24350             preventDefault: true,
24351             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24352         });
24353     //this.addSeparator();
24354         this.loading = this.navgroup.addItem({
24355             tooltip: this.refreshText,
24356             icon: 'fa fa-refresh',
24357             preventDefault: true,
24358             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24359         });
24360         
24361     },
24362
24363     // private
24364     updateInfo : function(){
24365         if(this.displayEl){
24366             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24367             var msg = count == 0 ?
24368                 this.emptyMsg :
24369                 String.format(
24370                     this.displayMsg,
24371                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24372                 );
24373             this.displayEl.update(msg);
24374         }
24375     },
24376
24377     // private
24378     onLoad : function(ds, r, o)
24379     {
24380         this.cursor = o.params ? o.params.start : 0;
24381         var d = this.getPageData(),
24382             ap = d.activePage,
24383             ps = d.pages;
24384         
24385         
24386         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24387         this.field.dom.value = ap;
24388         this.first.setDisabled(ap == 1);
24389         this.prev.setDisabled(ap == 1);
24390         this.next.setDisabled(ap == ps);
24391         this.last.setDisabled(ap == ps);
24392         this.loading.enable();
24393         this.updateInfo();
24394     },
24395
24396     // private
24397     getPageData : function(){
24398         var total = this.ds.getTotalCount();
24399         return {
24400             total : total,
24401             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24402             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24403         };
24404     },
24405
24406     // private
24407     onLoadError : function(){
24408         this.loading.enable();
24409     },
24410
24411     // private
24412     onPagingKeydown : function(e){
24413         var k = e.getKey();
24414         var d = this.getPageData();
24415         if(k == e.RETURN){
24416             var v = this.field.dom.value, pageNum;
24417             if(!v || isNaN(pageNum = parseInt(v, 10))){
24418                 this.field.dom.value = d.activePage;
24419                 return;
24420             }
24421             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24422             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24423             e.stopEvent();
24424         }
24425         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))
24426         {
24427           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24428           this.field.dom.value = pageNum;
24429           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24430           e.stopEvent();
24431         }
24432         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24433         {
24434           var v = this.field.dom.value, pageNum; 
24435           var increment = (e.shiftKey) ? 10 : 1;
24436           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24437                 increment *= -1;
24438           }
24439           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24440             this.field.dom.value = d.activePage;
24441             return;
24442           }
24443           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24444           {
24445             this.field.dom.value = parseInt(v, 10) + increment;
24446             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24447             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24448           }
24449           e.stopEvent();
24450         }
24451     },
24452
24453     // private
24454     beforeLoad : function(){
24455         if(this.loading){
24456             this.loading.disable();
24457         }
24458     },
24459
24460     // private
24461     onClick : function(which){
24462         
24463         var ds = this.ds;
24464         if (!ds) {
24465             return;
24466         }
24467         
24468         switch(which){
24469             case "first":
24470                 ds.load({params:{start: 0, limit: this.pageSize}});
24471             break;
24472             case "prev":
24473                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24474             break;
24475             case "next":
24476                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24477             break;
24478             case "last":
24479                 var total = ds.getTotalCount();
24480                 var extra = total % this.pageSize;
24481                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24482                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24483             break;
24484             case "refresh":
24485                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24486             break;
24487         }
24488     },
24489
24490     /**
24491      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24492      * @param {Roo.data.Store} store The data store to unbind
24493      */
24494     unbind : function(ds){
24495         ds.un("beforeload", this.beforeLoad, this);
24496         ds.un("load", this.onLoad, this);
24497         ds.un("loadexception", this.onLoadError, this);
24498         ds.un("remove", this.updateInfo, this);
24499         ds.un("add", this.updateInfo, this);
24500         this.ds = undefined;
24501     },
24502
24503     /**
24504      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24505      * @param {Roo.data.Store} store The data store to bind
24506      */
24507     bind : function(ds){
24508         ds.on("beforeload", this.beforeLoad, this);
24509         ds.on("load", this.onLoad, this);
24510         ds.on("loadexception", this.onLoadError, this);
24511         ds.on("remove", this.updateInfo, this);
24512         ds.on("add", this.updateInfo, this);
24513         this.ds = ds;
24514     }
24515 });/*
24516  * - LGPL
24517  *
24518  * element
24519  * 
24520  */
24521
24522 /**
24523  * @class Roo.bootstrap.MessageBar
24524  * @extends Roo.bootstrap.Component
24525  * Bootstrap MessageBar class
24526  * @cfg {String} html contents of the MessageBar
24527  * @cfg {String} weight (info | success | warning | danger) default info
24528  * @cfg {String} beforeClass insert the bar before the given class
24529  * @cfg {Boolean} closable (true | false) default false
24530  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24531  * 
24532  * @constructor
24533  * Create a new Element
24534  * @param {Object} config The config object
24535  */
24536
24537 Roo.bootstrap.MessageBar = function(config){
24538     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24539 };
24540
24541 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24542     
24543     html: '',
24544     weight: 'info',
24545     closable: false,
24546     fixed: false,
24547     beforeClass: 'bootstrap-sticky-wrap',
24548     
24549     getAutoCreate : function(){
24550         
24551         var cfg = {
24552             tag: 'div',
24553             cls: 'alert alert-dismissable alert-' + this.weight,
24554             cn: [
24555                 {
24556                     tag: 'span',
24557                     cls: 'message',
24558                     html: this.html || ''
24559                 }
24560             ]
24561         };
24562         
24563         if(this.fixed){
24564             cfg.cls += ' alert-messages-fixed';
24565         }
24566         
24567         if(this.closable){
24568             cfg.cn.push({
24569                 tag: 'button',
24570                 cls: 'close',
24571                 html: 'x'
24572             });
24573         }
24574         
24575         return cfg;
24576     },
24577     
24578     onRender : function(ct, position)
24579     {
24580         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24581         
24582         if(!this.el){
24583             var cfg = Roo.apply({},  this.getAutoCreate());
24584             cfg.id = Roo.id();
24585             
24586             if (this.cls) {
24587                 cfg.cls += ' ' + this.cls;
24588             }
24589             if (this.style) {
24590                 cfg.style = this.style;
24591             }
24592             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24593             
24594             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24595         }
24596         
24597         this.el.select('>button.close').on('click', this.hide, this);
24598         
24599     },
24600     
24601     show : function()
24602     {
24603         if (!this.rendered) {
24604             this.render();
24605         }
24606         
24607         this.el.show();
24608         
24609         this.fireEvent('show', this);
24610         
24611     },
24612     
24613     hide : function()
24614     {
24615         if (!this.rendered) {
24616             this.render();
24617         }
24618         
24619         this.el.hide();
24620         
24621         this.fireEvent('hide', this);
24622     },
24623     
24624     update : function()
24625     {
24626 //        var e = this.el.dom.firstChild;
24627 //        
24628 //        if(this.closable){
24629 //            e = e.nextSibling;
24630 //        }
24631 //        
24632 //        e.data = this.html || '';
24633
24634         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24635     }
24636    
24637 });
24638
24639  
24640
24641      /*
24642  * - LGPL
24643  *
24644  * Graph
24645  * 
24646  */
24647
24648
24649 /**
24650  * @class Roo.bootstrap.Graph
24651  * @extends Roo.bootstrap.Component
24652  * Bootstrap Graph class
24653 > Prameters
24654  -sm {number} sm 4
24655  -md {number} md 5
24656  @cfg {String} graphtype  bar | vbar | pie
24657  @cfg {number} g_x coodinator | centre x (pie)
24658  @cfg {number} g_y coodinator | centre y (pie)
24659  @cfg {number} g_r radius (pie)
24660  @cfg {number} g_height height of the chart (respected by all elements in the set)
24661  @cfg {number} g_width width of the chart (respected by all elements in the set)
24662  @cfg {Object} title The title of the chart
24663     
24664  -{Array}  values
24665  -opts (object) options for the chart 
24666      o {
24667      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24668      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24669      o vgutter (number)
24670      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.
24671      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24672      o to
24673      o stretch (boolean)
24674      o }
24675  -opts (object) options for the pie
24676      o{
24677      o cut
24678      o startAngle (number)
24679      o endAngle (number)
24680      } 
24681  *
24682  * @constructor
24683  * Create a new Input
24684  * @param {Object} config The config object
24685  */
24686
24687 Roo.bootstrap.Graph = function(config){
24688     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24689     
24690     this.addEvents({
24691         // img events
24692         /**
24693          * @event click
24694          * The img click event for the img.
24695          * @param {Roo.EventObject} e
24696          */
24697         "click" : true
24698     });
24699 };
24700
24701 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24702     
24703     sm: 4,
24704     md: 5,
24705     graphtype: 'bar',
24706     g_height: 250,
24707     g_width: 400,
24708     g_x: 50,
24709     g_y: 50,
24710     g_r: 30,
24711     opts:{
24712         //g_colors: this.colors,
24713         g_type: 'soft',
24714         g_gutter: '20%'
24715
24716     },
24717     title : false,
24718
24719     getAutoCreate : function(){
24720         
24721         var cfg = {
24722             tag: 'div',
24723             html : null
24724         };
24725         
24726         
24727         return  cfg;
24728     },
24729
24730     onRender : function(ct,position){
24731         
24732         
24733         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24734         
24735         if (typeof(Raphael) == 'undefined') {
24736             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24737             return;
24738         }
24739         
24740         this.raphael = Raphael(this.el.dom);
24741         
24742                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24743                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24744                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24745                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24746                 /*
24747                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24748                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24749                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24750                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24751                 
24752                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24753                 r.barchart(330, 10, 300, 220, data1);
24754                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24755                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24756                 */
24757                 
24758                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24759                 // r.barchart(30, 30, 560, 250,  xdata, {
24760                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24761                 //     axis : "0 0 1 1",
24762                 //     axisxlabels :  xdata
24763                 //     //yvalues : cols,
24764                    
24765                 // });
24766 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24767 //        
24768 //        this.load(null,xdata,{
24769 //                axis : "0 0 1 1",
24770 //                axisxlabels :  xdata
24771 //                });
24772
24773     },
24774
24775     load : function(graphtype,xdata,opts)
24776     {
24777         this.raphael.clear();
24778         if(!graphtype) {
24779             graphtype = this.graphtype;
24780         }
24781         if(!opts){
24782             opts = this.opts;
24783         }
24784         var r = this.raphael,
24785             fin = function () {
24786                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24787             },
24788             fout = function () {
24789                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24790             },
24791             pfin = function() {
24792                 this.sector.stop();
24793                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24794
24795                 if (this.label) {
24796                     this.label[0].stop();
24797                     this.label[0].attr({ r: 7.5 });
24798                     this.label[1].attr({ "font-weight": 800 });
24799                 }
24800             },
24801             pfout = function() {
24802                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24803
24804                 if (this.label) {
24805                     this.label[0].animate({ r: 5 }, 500, "bounce");
24806                     this.label[1].attr({ "font-weight": 400 });
24807                 }
24808             };
24809
24810         switch(graphtype){
24811             case 'bar':
24812                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24813                 break;
24814             case 'hbar':
24815                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24816                 break;
24817             case 'pie':
24818 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24819 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24820 //            
24821                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24822                 
24823                 break;
24824
24825         }
24826         
24827         if(this.title){
24828             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24829         }
24830         
24831     },
24832     
24833     setTitle: function(o)
24834     {
24835         this.title = o;
24836     },
24837     
24838     initEvents: function() {
24839         
24840         if(!this.href){
24841             this.el.on('click', this.onClick, this);
24842         }
24843     },
24844     
24845     onClick : function(e)
24846     {
24847         Roo.log('img onclick');
24848         this.fireEvent('click', this, e);
24849     }
24850    
24851 });
24852
24853  
24854 /*
24855  * - LGPL
24856  *
24857  * numberBox
24858  * 
24859  */
24860 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24861
24862 /**
24863  * @class Roo.bootstrap.dash.NumberBox
24864  * @extends Roo.bootstrap.Component
24865  * Bootstrap NumberBox class
24866  * @cfg {String} headline Box headline
24867  * @cfg {String} content Box content
24868  * @cfg {String} icon Box icon
24869  * @cfg {String} footer Footer text
24870  * @cfg {String} fhref Footer href
24871  * 
24872  * @constructor
24873  * Create a new NumberBox
24874  * @param {Object} config The config object
24875  */
24876
24877
24878 Roo.bootstrap.dash.NumberBox = function(config){
24879     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24880     
24881 };
24882
24883 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24884     
24885     headline : '',
24886     content : '',
24887     icon : '',
24888     footer : '',
24889     fhref : '',
24890     ficon : '',
24891     
24892     getAutoCreate : function(){
24893         
24894         var cfg = {
24895             tag : 'div',
24896             cls : 'small-box ',
24897             cn : [
24898                 {
24899                     tag : 'div',
24900                     cls : 'inner',
24901                     cn :[
24902                         {
24903                             tag : 'h3',
24904                             cls : 'roo-headline',
24905                             html : this.headline
24906                         },
24907                         {
24908                             tag : 'p',
24909                             cls : 'roo-content',
24910                             html : this.content
24911                         }
24912                     ]
24913                 }
24914             ]
24915         };
24916         
24917         if(this.icon){
24918             cfg.cn.push({
24919                 tag : 'div',
24920                 cls : 'icon',
24921                 cn :[
24922                     {
24923                         tag : 'i',
24924                         cls : 'ion ' + this.icon
24925                     }
24926                 ]
24927             });
24928         }
24929         
24930         if(this.footer){
24931             var footer = {
24932                 tag : 'a',
24933                 cls : 'small-box-footer',
24934                 href : this.fhref || '#',
24935                 html : this.footer
24936             };
24937             
24938             cfg.cn.push(footer);
24939             
24940         }
24941         
24942         return  cfg;
24943     },
24944
24945     onRender : function(ct,position){
24946         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24947
24948
24949        
24950                 
24951     },
24952
24953     setHeadline: function (value)
24954     {
24955         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24956     },
24957     
24958     setFooter: function (value, href)
24959     {
24960         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24961         
24962         if(href){
24963             this.el.select('a.small-box-footer',true).first().attr('href', href);
24964         }
24965         
24966     },
24967
24968     setContent: function (value)
24969     {
24970         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24971     },
24972
24973     initEvents: function() 
24974     {   
24975         
24976     }
24977     
24978 });
24979
24980  
24981 /*
24982  * - LGPL
24983  *
24984  * TabBox
24985  * 
24986  */
24987 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24988
24989 /**
24990  * @class Roo.bootstrap.dash.TabBox
24991  * @extends Roo.bootstrap.Component
24992  * Bootstrap TabBox class
24993  * @cfg {String} title Title of the TabBox
24994  * @cfg {String} icon Icon of the TabBox
24995  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24996  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24997  * 
24998  * @constructor
24999  * Create a new TabBox
25000  * @param {Object} config The config object
25001  */
25002
25003
25004 Roo.bootstrap.dash.TabBox = function(config){
25005     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25006     this.addEvents({
25007         // raw events
25008         /**
25009          * @event addpane
25010          * When a pane is added
25011          * @param {Roo.bootstrap.dash.TabPane} pane
25012          */
25013         "addpane" : true,
25014         /**
25015          * @event activatepane
25016          * When a pane is activated
25017          * @param {Roo.bootstrap.dash.TabPane} pane
25018          */
25019         "activatepane" : true
25020         
25021          
25022     });
25023     
25024     this.panes = [];
25025 };
25026
25027 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25028
25029     title : '',
25030     icon : false,
25031     showtabs : true,
25032     tabScrollable : false,
25033     
25034     getChildContainer : function()
25035     {
25036         return this.el.select('.tab-content', true).first();
25037     },
25038     
25039     getAutoCreate : function(){
25040         
25041         var header = {
25042             tag: 'li',
25043             cls: 'pull-left header',
25044             html: this.title,
25045             cn : []
25046         };
25047         
25048         if(this.icon){
25049             header.cn.push({
25050                 tag: 'i',
25051                 cls: 'fa ' + this.icon
25052             });
25053         }
25054         
25055         var h = {
25056             tag: 'ul',
25057             cls: 'nav nav-tabs pull-right',
25058             cn: [
25059                 header
25060             ]
25061         };
25062         
25063         if(this.tabScrollable){
25064             h = {
25065                 tag: 'div',
25066                 cls: 'tab-header',
25067                 cn: [
25068                     {
25069                         tag: 'ul',
25070                         cls: 'nav nav-tabs pull-right',
25071                         cn: [
25072                             header
25073                         ]
25074                     }
25075                 ]
25076             };
25077         }
25078         
25079         var cfg = {
25080             tag: 'div',
25081             cls: 'nav-tabs-custom',
25082             cn: [
25083                 h,
25084                 {
25085                     tag: 'div',
25086                     cls: 'tab-content no-padding',
25087                     cn: []
25088                 }
25089             ]
25090         };
25091
25092         return  cfg;
25093     },
25094     initEvents : function()
25095     {
25096         //Roo.log('add add pane handler');
25097         this.on('addpane', this.onAddPane, this);
25098     },
25099      /**
25100      * Updates the box title
25101      * @param {String} html to set the title to.
25102      */
25103     setTitle : function(value)
25104     {
25105         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25106     },
25107     onAddPane : function(pane)
25108     {
25109         this.panes.push(pane);
25110         //Roo.log('addpane');
25111         //Roo.log(pane);
25112         // tabs are rendere left to right..
25113         if(!this.showtabs){
25114             return;
25115         }
25116         
25117         var ctr = this.el.select('.nav-tabs', true).first();
25118          
25119          
25120         var existing = ctr.select('.nav-tab',true);
25121         var qty = existing.getCount();;
25122         
25123         
25124         var tab = ctr.createChild({
25125             tag : 'li',
25126             cls : 'nav-tab' + (qty ? '' : ' active'),
25127             cn : [
25128                 {
25129                     tag : 'a',
25130                     href:'#',
25131                     html : pane.title
25132                 }
25133             ]
25134         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25135         pane.tab = tab;
25136         
25137         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25138         if (!qty) {
25139             pane.el.addClass('active');
25140         }
25141         
25142                 
25143     },
25144     onTabClick : function(ev,un,ob,pane)
25145     {
25146         //Roo.log('tab - prev default');
25147         ev.preventDefault();
25148         
25149         
25150         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25151         pane.tab.addClass('active');
25152         //Roo.log(pane.title);
25153         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25154         // technically we should have a deactivate event.. but maybe add later.
25155         // and it should not de-activate the selected tab...
25156         this.fireEvent('activatepane', pane);
25157         pane.el.addClass('active');
25158         pane.fireEvent('activate');
25159         
25160         
25161     },
25162     
25163     getActivePane : function()
25164     {
25165         var r = false;
25166         Roo.each(this.panes, function(p) {
25167             if(p.el.hasClass('active')){
25168                 r = p;
25169                 return false;
25170             }
25171             
25172             return;
25173         });
25174         
25175         return r;
25176     }
25177     
25178     
25179 });
25180
25181  
25182 /*
25183  * - LGPL
25184  *
25185  * Tab pane
25186  * 
25187  */
25188 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25189 /**
25190  * @class Roo.bootstrap.TabPane
25191  * @extends Roo.bootstrap.Component
25192  * Bootstrap TabPane class
25193  * @cfg {Boolean} active (false | true) Default false
25194  * @cfg {String} title title of panel
25195
25196  * 
25197  * @constructor
25198  * Create a new TabPane
25199  * @param {Object} config The config object
25200  */
25201
25202 Roo.bootstrap.dash.TabPane = function(config){
25203     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25204     
25205     this.addEvents({
25206         // raw events
25207         /**
25208          * @event activate
25209          * When a pane is activated
25210          * @param {Roo.bootstrap.dash.TabPane} pane
25211          */
25212         "activate" : true
25213          
25214     });
25215 };
25216
25217 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25218     
25219     active : false,
25220     title : '',
25221     
25222     // the tabBox that this is attached to.
25223     tab : false,
25224      
25225     getAutoCreate : function() 
25226     {
25227         var cfg = {
25228             tag: 'div',
25229             cls: 'tab-pane'
25230         };
25231         
25232         if(this.active){
25233             cfg.cls += ' active';
25234         }
25235         
25236         return cfg;
25237     },
25238     initEvents  : function()
25239     {
25240         //Roo.log('trigger add pane handler');
25241         this.parent().fireEvent('addpane', this)
25242     },
25243     
25244      /**
25245      * Updates the tab title 
25246      * @param {String} html to set the title to.
25247      */
25248     setTitle: function(str)
25249     {
25250         if (!this.tab) {
25251             return;
25252         }
25253         this.title = str;
25254         this.tab.select('a', true).first().dom.innerHTML = str;
25255         
25256     }
25257     
25258     
25259     
25260 });
25261
25262  
25263
25264
25265  /*
25266  * - LGPL
25267  *
25268  * menu
25269  * 
25270  */
25271 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25272
25273 /**
25274  * @class Roo.bootstrap.menu.Menu
25275  * @extends Roo.bootstrap.Component
25276  * Bootstrap Menu class - container for Menu
25277  * @cfg {String} html Text of the menu
25278  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25279  * @cfg {String} icon Font awesome icon
25280  * @cfg {String} pos Menu align to (top | bottom) default bottom
25281  * 
25282  * 
25283  * @constructor
25284  * Create a new Menu
25285  * @param {Object} config The config object
25286  */
25287
25288
25289 Roo.bootstrap.menu.Menu = function(config){
25290     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25291     
25292     this.addEvents({
25293         /**
25294          * @event beforeshow
25295          * Fires before this menu is displayed
25296          * @param {Roo.bootstrap.menu.Menu} this
25297          */
25298         beforeshow : true,
25299         /**
25300          * @event beforehide
25301          * Fires before this menu is hidden
25302          * @param {Roo.bootstrap.menu.Menu} this
25303          */
25304         beforehide : true,
25305         /**
25306          * @event show
25307          * Fires after this menu is displayed
25308          * @param {Roo.bootstrap.menu.Menu} this
25309          */
25310         show : true,
25311         /**
25312          * @event hide
25313          * Fires after this menu is hidden
25314          * @param {Roo.bootstrap.menu.Menu} this
25315          */
25316         hide : true,
25317         /**
25318          * @event click
25319          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25320          * @param {Roo.bootstrap.menu.Menu} this
25321          * @param {Roo.EventObject} e
25322          */
25323         click : true
25324     });
25325     
25326 };
25327
25328 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25329     
25330     submenu : false,
25331     html : '',
25332     weight : 'default',
25333     icon : false,
25334     pos : 'bottom',
25335     
25336     
25337     getChildContainer : function() {
25338         if(this.isSubMenu){
25339             return this.el;
25340         }
25341         
25342         return this.el.select('ul.dropdown-menu', true).first();  
25343     },
25344     
25345     getAutoCreate : function()
25346     {
25347         var text = [
25348             {
25349                 tag : 'span',
25350                 cls : 'roo-menu-text',
25351                 html : this.html
25352             }
25353         ];
25354         
25355         if(this.icon){
25356             text.unshift({
25357                 tag : 'i',
25358                 cls : 'fa ' + this.icon
25359             })
25360         }
25361         
25362         
25363         var cfg = {
25364             tag : 'div',
25365             cls : 'btn-group',
25366             cn : [
25367                 {
25368                     tag : 'button',
25369                     cls : 'dropdown-button btn btn-' + this.weight,
25370                     cn : text
25371                 },
25372                 {
25373                     tag : 'button',
25374                     cls : 'dropdown-toggle btn btn-' + this.weight,
25375                     cn : [
25376                         {
25377                             tag : 'span',
25378                             cls : 'caret'
25379                         }
25380                     ]
25381                 },
25382                 {
25383                     tag : 'ul',
25384                     cls : 'dropdown-menu'
25385                 }
25386             ]
25387             
25388         };
25389         
25390         if(this.pos == 'top'){
25391             cfg.cls += ' dropup';
25392         }
25393         
25394         if(this.isSubMenu){
25395             cfg = {
25396                 tag : 'ul',
25397                 cls : 'dropdown-menu'
25398             }
25399         }
25400         
25401         return cfg;
25402     },
25403     
25404     onRender : function(ct, position)
25405     {
25406         this.isSubMenu = ct.hasClass('dropdown-submenu');
25407         
25408         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25409     },
25410     
25411     initEvents : function() 
25412     {
25413         if(this.isSubMenu){
25414             return;
25415         }
25416         
25417         this.hidden = true;
25418         
25419         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25420         this.triggerEl.on('click', this.onTriggerPress, this);
25421         
25422         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25423         this.buttonEl.on('click', this.onClick, this);
25424         
25425     },
25426     
25427     list : function()
25428     {
25429         if(this.isSubMenu){
25430             return this.el;
25431         }
25432         
25433         return this.el.select('ul.dropdown-menu', true).first();
25434     },
25435     
25436     onClick : function(e)
25437     {
25438         this.fireEvent("click", this, e);
25439     },
25440     
25441     onTriggerPress  : function(e)
25442     {   
25443         if (this.isVisible()) {
25444             this.hide();
25445         } else {
25446             this.show();
25447         }
25448     },
25449     
25450     isVisible : function(){
25451         return !this.hidden;
25452     },
25453     
25454     show : function()
25455     {
25456         this.fireEvent("beforeshow", this);
25457         
25458         this.hidden = false;
25459         this.el.addClass('open');
25460         
25461         Roo.get(document).on("mouseup", this.onMouseUp, this);
25462         
25463         this.fireEvent("show", this);
25464         
25465         
25466     },
25467     
25468     hide : function()
25469     {
25470         this.fireEvent("beforehide", this);
25471         
25472         this.hidden = true;
25473         this.el.removeClass('open');
25474         
25475         Roo.get(document).un("mouseup", this.onMouseUp);
25476         
25477         this.fireEvent("hide", this);
25478     },
25479     
25480     onMouseUp : function()
25481     {
25482         this.hide();
25483     }
25484     
25485 });
25486
25487  
25488  /*
25489  * - LGPL
25490  *
25491  * menu item
25492  * 
25493  */
25494 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25495
25496 /**
25497  * @class Roo.bootstrap.menu.Item
25498  * @extends Roo.bootstrap.Component
25499  * Bootstrap MenuItem class
25500  * @cfg {Boolean} submenu (true | false) default false
25501  * @cfg {String} html text of the item
25502  * @cfg {String} href the link
25503  * @cfg {Boolean} disable (true | false) default false
25504  * @cfg {Boolean} preventDefault (true | false) default true
25505  * @cfg {String} icon Font awesome icon
25506  * @cfg {String} pos Submenu align to (left | right) default right 
25507  * 
25508  * 
25509  * @constructor
25510  * Create a new Item
25511  * @param {Object} config The config object
25512  */
25513
25514
25515 Roo.bootstrap.menu.Item = function(config){
25516     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25517     this.addEvents({
25518         /**
25519          * @event mouseover
25520          * Fires when the mouse is hovering over this menu
25521          * @param {Roo.bootstrap.menu.Item} this
25522          * @param {Roo.EventObject} e
25523          */
25524         mouseover : true,
25525         /**
25526          * @event mouseout
25527          * Fires when the mouse exits this menu
25528          * @param {Roo.bootstrap.menu.Item} this
25529          * @param {Roo.EventObject} e
25530          */
25531         mouseout : true,
25532         // raw events
25533         /**
25534          * @event click
25535          * The raw click event for the entire grid.
25536          * @param {Roo.EventObject} e
25537          */
25538         click : true
25539     });
25540 };
25541
25542 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25543     
25544     submenu : false,
25545     href : '',
25546     html : '',
25547     preventDefault: true,
25548     disable : false,
25549     icon : false,
25550     pos : 'right',
25551     
25552     getAutoCreate : function()
25553     {
25554         var text = [
25555             {
25556                 tag : 'span',
25557                 cls : 'roo-menu-item-text',
25558                 html : this.html
25559             }
25560         ];
25561         
25562         if(this.icon){
25563             text.unshift({
25564                 tag : 'i',
25565                 cls : 'fa ' + this.icon
25566             })
25567         }
25568         
25569         var cfg = {
25570             tag : 'li',
25571             cn : [
25572                 {
25573                     tag : 'a',
25574                     href : this.href || '#',
25575                     cn : text
25576                 }
25577             ]
25578         };
25579         
25580         if(this.disable){
25581             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25582         }
25583         
25584         if(this.submenu){
25585             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25586             
25587             if(this.pos == 'left'){
25588                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25589             }
25590         }
25591         
25592         return cfg;
25593     },
25594     
25595     initEvents : function() 
25596     {
25597         this.el.on('mouseover', this.onMouseOver, this);
25598         this.el.on('mouseout', this.onMouseOut, this);
25599         
25600         this.el.select('a', true).first().on('click', this.onClick, this);
25601         
25602     },
25603     
25604     onClick : function(e)
25605     {
25606         if(this.preventDefault){
25607             e.preventDefault();
25608         }
25609         
25610         this.fireEvent("click", this, e);
25611     },
25612     
25613     onMouseOver : function(e)
25614     {
25615         if(this.submenu && this.pos == 'left'){
25616             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25617         }
25618         
25619         this.fireEvent("mouseover", this, e);
25620     },
25621     
25622     onMouseOut : function(e)
25623     {
25624         this.fireEvent("mouseout", this, e);
25625     }
25626 });
25627
25628  
25629
25630  /*
25631  * - LGPL
25632  *
25633  * menu separator
25634  * 
25635  */
25636 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25637
25638 /**
25639  * @class Roo.bootstrap.menu.Separator
25640  * @extends Roo.bootstrap.Component
25641  * Bootstrap Separator class
25642  * 
25643  * @constructor
25644  * Create a new Separator
25645  * @param {Object} config The config object
25646  */
25647
25648
25649 Roo.bootstrap.menu.Separator = function(config){
25650     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25651 };
25652
25653 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25654     
25655     getAutoCreate : function(){
25656         var cfg = {
25657             tag : 'li',
25658             cls: 'divider'
25659         };
25660         
25661         return cfg;
25662     }
25663    
25664 });
25665
25666  
25667
25668  /*
25669  * - LGPL
25670  *
25671  * Tooltip
25672  * 
25673  */
25674
25675 /**
25676  * @class Roo.bootstrap.Tooltip
25677  * Bootstrap Tooltip class
25678  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25679  * to determine which dom element triggers the tooltip.
25680  * 
25681  * It needs to add support for additional attributes like tooltip-position
25682  * 
25683  * @constructor
25684  * Create a new Toolti
25685  * @param {Object} config The config object
25686  */
25687
25688 Roo.bootstrap.Tooltip = function(config){
25689     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25690     
25691     this.alignment = Roo.bootstrap.Tooltip.alignment;
25692     
25693     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25694         this.alignment = config.alignment;
25695     }
25696     
25697 };
25698
25699 Roo.apply(Roo.bootstrap.Tooltip, {
25700     /**
25701      * @function init initialize tooltip monitoring.
25702      * @static
25703      */
25704     currentEl : false,
25705     currentTip : false,
25706     currentRegion : false,
25707     
25708     //  init : delay?
25709     
25710     init : function()
25711     {
25712         Roo.get(document).on('mouseover', this.enter ,this);
25713         Roo.get(document).on('mouseout', this.leave, this);
25714          
25715         
25716         this.currentTip = new Roo.bootstrap.Tooltip();
25717     },
25718     
25719     enter : function(ev)
25720     {
25721         var dom = ev.getTarget();
25722         
25723         //Roo.log(['enter',dom]);
25724         var el = Roo.fly(dom);
25725         if (this.currentEl) {
25726             //Roo.log(dom);
25727             //Roo.log(this.currentEl);
25728             //Roo.log(this.currentEl.contains(dom));
25729             if (this.currentEl == el) {
25730                 return;
25731             }
25732             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25733                 return;
25734             }
25735
25736         }
25737         
25738         if (this.currentTip.el) {
25739             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25740         }    
25741         //Roo.log(ev);
25742         
25743         if(!el || el.dom == document){
25744             return;
25745         }
25746         
25747         var bindEl = el;
25748         
25749         // you can not look for children, as if el is the body.. then everythign is the child..
25750         if (!el.attr('tooltip')) { //
25751             if (!el.select("[tooltip]").elements.length) {
25752                 return;
25753             }
25754             // is the mouse over this child...?
25755             bindEl = el.select("[tooltip]").first();
25756             var xy = ev.getXY();
25757             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25758                 //Roo.log("not in region.");
25759                 return;
25760             }
25761             //Roo.log("child element over..");
25762             
25763         }
25764         this.currentEl = bindEl;
25765         this.currentTip.bind(bindEl);
25766         this.currentRegion = Roo.lib.Region.getRegion(dom);
25767         this.currentTip.enter();
25768         
25769     },
25770     leave : function(ev)
25771     {
25772         var dom = ev.getTarget();
25773         //Roo.log(['leave',dom]);
25774         if (!this.currentEl) {
25775             return;
25776         }
25777         
25778         
25779         if (dom != this.currentEl.dom) {
25780             return;
25781         }
25782         var xy = ev.getXY();
25783         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25784             return;
25785         }
25786         // only activate leave if mouse cursor is outside... bounding box..
25787         
25788         
25789         
25790         
25791         if (this.currentTip) {
25792             this.currentTip.leave();
25793         }
25794         //Roo.log('clear currentEl');
25795         this.currentEl = false;
25796         
25797         
25798     },
25799     alignment : {
25800         'left' : ['r-l', [-2,0], 'right'],
25801         'right' : ['l-r', [2,0], 'left'],
25802         'bottom' : ['t-b', [0,2], 'top'],
25803         'top' : [ 'b-t', [0,-2], 'bottom']
25804     }
25805     
25806 });
25807
25808
25809 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25810     
25811     
25812     bindEl : false,
25813     
25814     delay : null, // can be { show : 300 , hide: 500}
25815     
25816     timeout : null,
25817     
25818     hoverState : null, //???
25819     
25820     placement : 'bottom', 
25821     
25822     alignment : false,
25823     
25824     getAutoCreate : function(){
25825     
25826         var cfg = {
25827            cls : 'tooltip',
25828            role : 'tooltip',
25829            cn : [
25830                 {
25831                     cls : 'tooltip-arrow'
25832                 },
25833                 {
25834                     cls : 'tooltip-inner'
25835                 }
25836            ]
25837         };
25838         
25839         return cfg;
25840     },
25841     bind : function(el)
25842     {
25843         this.bindEl = el;
25844     },
25845       
25846     
25847     enter : function () {
25848        
25849         if (this.timeout != null) {
25850             clearTimeout(this.timeout);
25851         }
25852         
25853         this.hoverState = 'in';
25854          //Roo.log("enter - show");
25855         if (!this.delay || !this.delay.show) {
25856             this.show();
25857             return;
25858         }
25859         var _t = this;
25860         this.timeout = setTimeout(function () {
25861             if (_t.hoverState == 'in') {
25862                 _t.show();
25863             }
25864         }, this.delay.show);
25865     },
25866     leave : function()
25867     {
25868         clearTimeout(this.timeout);
25869     
25870         this.hoverState = 'out';
25871          if (!this.delay || !this.delay.hide) {
25872             this.hide();
25873             return;
25874         }
25875        
25876         var _t = this;
25877         this.timeout = setTimeout(function () {
25878             //Roo.log("leave - timeout");
25879             
25880             if (_t.hoverState == 'out') {
25881                 _t.hide();
25882                 Roo.bootstrap.Tooltip.currentEl = false;
25883             }
25884         }, delay);
25885     },
25886     
25887     show : function (msg)
25888     {
25889         if (!this.el) {
25890             this.render(document.body);
25891         }
25892         // set content.
25893         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25894         
25895         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25896         
25897         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25898         
25899         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25900         
25901         var placement = typeof this.placement == 'function' ?
25902             this.placement.call(this, this.el, on_el) :
25903             this.placement;
25904             
25905         var autoToken = /\s?auto?\s?/i;
25906         var autoPlace = autoToken.test(placement);
25907         if (autoPlace) {
25908             placement = placement.replace(autoToken, '') || 'top';
25909         }
25910         
25911         //this.el.detach()
25912         //this.el.setXY([0,0]);
25913         this.el.show();
25914         //this.el.dom.style.display='block';
25915         
25916         //this.el.appendTo(on_el);
25917         
25918         var p = this.getPosition();
25919         var box = this.el.getBox();
25920         
25921         if (autoPlace) {
25922             // fixme..
25923         }
25924         
25925         var align = this.alignment[placement];
25926         
25927         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25928         
25929         if(placement == 'top' || placement == 'bottom'){
25930             if(xy[0] < 0){
25931                 placement = 'right';
25932             }
25933             
25934             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25935                 placement = 'left';
25936             }
25937             
25938             var scroll = Roo.select('body', true).first().getScroll();
25939             
25940             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25941                 placement = 'top';
25942             }
25943             
25944         }
25945         
25946         this.el.alignTo(this.bindEl, align[0],align[1]);
25947         //var arrow = this.el.select('.arrow',true).first();
25948         //arrow.set(align[2], 
25949         
25950         this.el.addClass(placement);
25951         
25952         this.el.addClass('in fade');
25953         
25954         this.hoverState = null;
25955         
25956         if (this.el.hasClass('fade')) {
25957             // fade it?
25958         }
25959         
25960     },
25961     hide : function()
25962     {
25963          
25964         if (!this.el) {
25965             return;
25966         }
25967         //this.el.setXY([0,0]);
25968         this.el.removeClass('in');
25969         //this.el.hide();
25970         
25971     }
25972     
25973 });
25974  
25975
25976  /*
25977  * - LGPL
25978  *
25979  * Location Picker
25980  * 
25981  */
25982
25983 /**
25984  * @class Roo.bootstrap.LocationPicker
25985  * @extends Roo.bootstrap.Component
25986  * Bootstrap LocationPicker class
25987  * @cfg {Number} latitude Position when init default 0
25988  * @cfg {Number} longitude Position when init default 0
25989  * @cfg {Number} zoom default 15
25990  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25991  * @cfg {Boolean} mapTypeControl default false
25992  * @cfg {Boolean} disableDoubleClickZoom default false
25993  * @cfg {Boolean} scrollwheel default true
25994  * @cfg {Boolean} streetViewControl default false
25995  * @cfg {Number} radius default 0
25996  * @cfg {String} locationName
25997  * @cfg {Boolean} draggable default true
25998  * @cfg {Boolean} enableAutocomplete default false
25999  * @cfg {Boolean} enableReverseGeocode default true
26000  * @cfg {String} markerTitle
26001  * 
26002  * @constructor
26003  * Create a new LocationPicker
26004  * @param {Object} config The config object
26005  */
26006
26007
26008 Roo.bootstrap.LocationPicker = function(config){
26009     
26010     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26011     
26012     this.addEvents({
26013         /**
26014          * @event initial
26015          * Fires when the picker initialized.
26016          * @param {Roo.bootstrap.LocationPicker} this
26017          * @param {Google Location} location
26018          */
26019         initial : true,
26020         /**
26021          * @event positionchanged
26022          * Fires when the picker position changed.
26023          * @param {Roo.bootstrap.LocationPicker} this
26024          * @param {Google Location} location
26025          */
26026         positionchanged : true,
26027         /**
26028          * @event resize
26029          * Fires when the map resize.
26030          * @param {Roo.bootstrap.LocationPicker} this
26031          */
26032         resize : true,
26033         /**
26034          * @event show
26035          * Fires when the map show.
26036          * @param {Roo.bootstrap.LocationPicker} this
26037          */
26038         show : true,
26039         /**
26040          * @event hide
26041          * Fires when the map hide.
26042          * @param {Roo.bootstrap.LocationPicker} this
26043          */
26044         hide : true,
26045         /**
26046          * @event mapClick
26047          * Fires when click the map.
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          * @param {Map event} e
26050          */
26051         mapClick : true,
26052         /**
26053          * @event mapRightClick
26054          * Fires when right click the map.
26055          * @param {Roo.bootstrap.LocationPicker} this
26056          * @param {Map event} e
26057          */
26058         mapRightClick : true,
26059         /**
26060          * @event markerClick
26061          * Fires when click the marker.
26062          * @param {Roo.bootstrap.LocationPicker} this
26063          * @param {Map event} e
26064          */
26065         markerClick : true,
26066         /**
26067          * @event markerRightClick
26068          * Fires when right click the marker.
26069          * @param {Roo.bootstrap.LocationPicker} this
26070          * @param {Map event} e
26071          */
26072         markerRightClick : true,
26073         /**
26074          * @event OverlayViewDraw
26075          * Fires when OverlayView Draw
26076          * @param {Roo.bootstrap.LocationPicker} this
26077          */
26078         OverlayViewDraw : true,
26079         /**
26080          * @event OverlayViewOnAdd
26081          * Fires when OverlayView Draw
26082          * @param {Roo.bootstrap.LocationPicker} this
26083          */
26084         OverlayViewOnAdd : true,
26085         /**
26086          * @event OverlayViewOnRemove
26087          * Fires when OverlayView Draw
26088          * @param {Roo.bootstrap.LocationPicker} this
26089          */
26090         OverlayViewOnRemove : true,
26091         /**
26092          * @event OverlayViewShow
26093          * Fires when OverlayView Draw
26094          * @param {Roo.bootstrap.LocationPicker} this
26095          * @param {Pixel} cpx
26096          */
26097         OverlayViewShow : true,
26098         /**
26099          * @event OverlayViewHide
26100          * Fires when OverlayView Draw
26101          * @param {Roo.bootstrap.LocationPicker} this
26102          */
26103         OverlayViewHide : true,
26104         /**
26105          * @event loadexception
26106          * Fires when load google lib failed.
26107          * @param {Roo.bootstrap.LocationPicker} this
26108          */
26109         loadexception : true
26110     });
26111         
26112 };
26113
26114 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26115     
26116     gMapContext: false,
26117     
26118     latitude: 0,
26119     longitude: 0,
26120     zoom: 15,
26121     mapTypeId: false,
26122     mapTypeControl: false,
26123     disableDoubleClickZoom: false,
26124     scrollwheel: true,
26125     streetViewControl: false,
26126     radius: 0,
26127     locationName: '',
26128     draggable: true,
26129     enableAutocomplete: false,
26130     enableReverseGeocode: true,
26131     markerTitle: '',
26132     
26133     getAutoCreate: function()
26134     {
26135
26136         var cfg = {
26137             tag: 'div',
26138             cls: 'roo-location-picker'
26139         };
26140         
26141         return cfg
26142     },
26143     
26144     initEvents: function(ct, position)
26145     {       
26146         if(!this.el.getWidth() || this.isApplied()){
26147             return;
26148         }
26149         
26150         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26151         
26152         this.initial();
26153     },
26154     
26155     initial: function()
26156     {
26157         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26158             this.fireEvent('loadexception', this);
26159             return;
26160         }
26161         
26162         if(!this.mapTypeId){
26163             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26164         }
26165         
26166         this.gMapContext = this.GMapContext();
26167         
26168         this.initOverlayView();
26169         
26170         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26171         
26172         var _this = this;
26173                 
26174         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26175             _this.setPosition(_this.gMapContext.marker.position);
26176         });
26177         
26178         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26179             _this.fireEvent('mapClick', this, event);
26180             
26181         });
26182
26183         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26184             _this.fireEvent('mapRightClick', this, event);
26185             
26186         });
26187         
26188         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26189             _this.fireEvent('markerClick', this, event);
26190             
26191         });
26192
26193         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26194             _this.fireEvent('markerRightClick', this, event);
26195             
26196         });
26197         
26198         this.setPosition(this.gMapContext.location);
26199         
26200         this.fireEvent('initial', this, this.gMapContext.location);
26201     },
26202     
26203     initOverlayView: function()
26204     {
26205         var _this = this;
26206         
26207         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26208             
26209             draw: function()
26210             {
26211                 _this.fireEvent('OverlayViewDraw', _this);
26212             },
26213             
26214             onAdd: function()
26215             {
26216                 _this.fireEvent('OverlayViewOnAdd', _this);
26217             },
26218             
26219             onRemove: function()
26220             {
26221                 _this.fireEvent('OverlayViewOnRemove', _this);
26222             },
26223             
26224             show: function(cpx)
26225             {
26226                 _this.fireEvent('OverlayViewShow', _this, cpx);
26227             },
26228             
26229             hide: function()
26230             {
26231                 _this.fireEvent('OverlayViewHide', _this);
26232             }
26233             
26234         });
26235     },
26236     
26237     fromLatLngToContainerPixel: function(event)
26238     {
26239         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26240     },
26241     
26242     isApplied: function() 
26243     {
26244         return this.getGmapContext() == false ? false : true;
26245     },
26246     
26247     getGmapContext: function() 
26248     {
26249         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26250     },
26251     
26252     GMapContext: function() 
26253     {
26254         var position = new google.maps.LatLng(this.latitude, this.longitude);
26255         
26256         var _map = new google.maps.Map(this.el.dom, {
26257             center: position,
26258             zoom: this.zoom,
26259             mapTypeId: this.mapTypeId,
26260             mapTypeControl: this.mapTypeControl,
26261             disableDoubleClickZoom: this.disableDoubleClickZoom,
26262             scrollwheel: this.scrollwheel,
26263             streetViewControl: this.streetViewControl,
26264             locationName: this.locationName,
26265             draggable: this.draggable,
26266             enableAutocomplete: this.enableAutocomplete,
26267             enableReverseGeocode: this.enableReverseGeocode
26268         });
26269         
26270         var _marker = new google.maps.Marker({
26271             position: position,
26272             map: _map,
26273             title: this.markerTitle,
26274             draggable: this.draggable
26275         });
26276         
26277         return {
26278             map: _map,
26279             marker: _marker,
26280             circle: null,
26281             location: position,
26282             radius: this.radius,
26283             locationName: this.locationName,
26284             addressComponents: {
26285                 formatted_address: null,
26286                 addressLine1: null,
26287                 addressLine2: null,
26288                 streetName: null,
26289                 streetNumber: null,
26290                 city: null,
26291                 district: null,
26292                 state: null,
26293                 stateOrProvince: null
26294             },
26295             settings: this,
26296             domContainer: this.el.dom,
26297             geodecoder: new google.maps.Geocoder()
26298         };
26299     },
26300     
26301     drawCircle: function(center, radius, options) 
26302     {
26303         if (this.gMapContext.circle != null) {
26304             this.gMapContext.circle.setMap(null);
26305         }
26306         if (radius > 0) {
26307             radius *= 1;
26308             options = Roo.apply({}, options, {
26309                 strokeColor: "#0000FF",
26310                 strokeOpacity: .35,
26311                 strokeWeight: 2,
26312                 fillColor: "#0000FF",
26313                 fillOpacity: .2
26314             });
26315             
26316             options.map = this.gMapContext.map;
26317             options.radius = radius;
26318             options.center = center;
26319             this.gMapContext.circle = new google.maps.Circle(options);
26320             return this.gMapContext.circle;
26321         }
26322         
26323         return null;
26324     },
26325     
26326     setPosition: function(location) 
26327     {
26328         this.gMapContext.location = location;
26329         this.gMapContext.marker.setPosition(location);
26330         this.gMapContext.map.panTo(location);
26331         this.drawCircle(location, this.gMapContext.radius, {});
26332         
26333         var _this = this;
26334         
26335         if (this.gMapContext.settings.enableReverseGeocode) {
26336             this.gMapContext.geodecoder.geocode({
26337                 latLng: this.gMapContext.location
26338             }, function(results, status) {
26339                 
26340                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26341                     _this.gMapContext.locationName = results[0].formatted_address;
26342                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26343                     
26344                     _this.fireEvent('positionchanged', this, location);
26345                 }
26346             });
26347             
26348             return;
26349         }
26350         
26351         this.fireEvent('positionchanged', this, location);
26352     },
26353     
26354     resize: function()
26355     {
26356         google.maps.event.trigger(this.gMapContext.map, "resize");
26357         
26358         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26359         
26360         this.fireEvent('resize', this);
26361     },
26362     
26363     setPositionByLatLng: function(latitude, longitude)
26364     {
26365         this.setPosition(new google.maps.LatLng(latitude, longitude));
26366     },
26367     
26368     getCurrentPosition: function() 
26369     {
26370         return {
26371             latitude: this.gMapContext.location.lat(),
26372             longitude: this.gMapContext.location.lng()
26373         };
26374     },
26375     
26376     getAddressName: function() 
26377     {
26378         return this.gMapContext.locationName;
26379     },
26380     
26381     getAddressComponents: function() 
26382     {
26383         return this.gMapContext.addressComponents;
26384     },
26385     
26386     address_component_from_google_geocode: function(address_components) 
26387     {
26388         var result = {};
26389         
26390         for (var i = 0; i < address_components.length; i++) {
26391             var component = address_components[i];
26392             if (component.types.indexOf("postal_code") >= 0) {
26393                 result.postalCode = component.short_name;
26394             } else if (component.types.indexOf("street_number") >= 0) {
26395                 result.streetNumber = component.short_name;
26396             } else if (component.types.indexOf("route") >= 0) {
26397                 result.streetName = component.short_name;
26398             } else if (component.types.indexOf("neighborhood") >= 0) {
26399                 result.city = component.short_name;
26400             } else if (component.types.indexOf("locality") >= 0) {
26401                 result.city = component.short_name;
26402             } else if (component.types.indexOf("sublocality") >= 0) {
26403                 result.district = component.short_name;
26404             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26405                 result.stateOrProvince = component.short_name;
26406             } else if (component.types.indexOf("country") >= 0) {
26407                 result.country = component.short_name;
26408             }
26409         }
26410         
26411         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26412         result.addressLine2 = "";
26413         return result;
26414     },
26415     
26416     setZoomLevel: function(zoom)
26417     {
26418         this.gMapContext.map.setZoom(zoom);
26419     },
26420     
26421     show: function()
26422     {
26423         if(!this.el){
26424             return;
26425         }
26426         
26427         this.el.show();
26428         
26429         this.resize();
26430         
26431         this.fireEvent('show', this);
26432     },
26433     
26434     hide: function()
26435     {
26436         if(!this.el){
26437             return;
26438         }
26439         
26440         this.el.hide();
26441         
26442         this.fireEvent('hide', this);
26443     }
26444     
26445 });
26446
26447 Roo.apply(Roo.bootstrap.LocationPicker, {
26448     
26449     OverlayView : function(map, options)
26450     {
26451         options = options || {};
26452         
26453         this.setMap(map);
26454     }
26455     
26456     
26457 });/*
26458  * - LGPL
26459  *
26460  * Alert
26461  * 
26462  */
26463
26464 /**
26465  * @class Roo.bootstrap.Alert
26466  * @extends Roo.bootstrap.Component
26467  * Bootstrap Alert class
26468  * @cfg {String} title The title of alert
26469  * @cfg {String} html The content of alert
26470  * @cfg {String} weight (  success | info | warning | danger )
26471  * @cfg {String} faicon font-awesomeicon
26472  * 
26473  * @constructor
26474  * Create a new alert
26475  * @param {Object} config The config object
26476  */
26477
26478
26479 Roo.bootstrap.Alert = function(config){
26480     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26481     
26482 };
26483
26484 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26485     
26486     title: '',
26487     html: '',
26488     weight: false,
26489     faicon: false,
26490     
26491     getAutoCreate : function()
26492     {
26493         
26494         var cfg = {
26495             tag : 'div',
26496             cls : 'alert',
26497             cn : [
26498                 {
26499                     tag : 'i',
26500                     cls : 'roo-alert-icon'
26501                     
26502                 },
26503                 {
26504                     tag : 'b',
26505                     cls : 'roo-alert-title',
26506                     html : this.title
26507                 },
26508                 {
26509                     tag : 'span',
26510                     cls : 'roo-alert-text',
26511                     html : this.html
26512                 }
26513             ]
26514         };
26515         
26516         if(this.faicon){
26517             cfg.cn[0].cls += ' fa ' + this.faicon;
26518         }
26519         
26520         if(this.weight){
26521             cfg.cls += ' alert-' + this.weight;
26522         }
26523         
26524         return cfg;
26525     },
26526     
26527     initEvents: function() 
26528     {
26529         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26530     },
26531     
26532     setTitle : function(str)
26533     {
26534         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26535     },
26536     
26537     setText : function(str)
26538     {
26539         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26540     },
26541     
26542     setWeight : function(weight)
26543     {
26544         if(this.weight){
26545             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26546         }
26547         
26548         this.weight = weight;
26549         
26550         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26551     },
26552     
26553     setIcon : function(icon)
26554     {
26555         if(this.faicon){
26556             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26557         }
26558         
26559         this.faicon = icon;
26560         
26561         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26562     },
26563     
26564     hide: function() 
26565     {
26566         this.el.hide();   
26567     },
26568     
26569     show: function() 
26570     {  
26571         this.el.show();   
26572     }
26573     
26574 });
26575
26576  
26577 /*
26578 * Licence: LGPL
26579 */
26580
26581 /**
26582  * @class Roo.bootstrap.UploadCropbox
26583  * @extends Roo.bootstrap.Component
26584  * Bootstrap UploadCropbox class
26585  * @cfg {String} emptyText show when image has been loaded
26586  * @cfg {String} rotateNotify show when image too small to rotate
26587  * @cfg {Number} errorTimeout default 3000
26588  * @cfg {Number} minWidth default 300
26589  * @cfg {Number} minHeight default 300
26590  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26591  * @cfg {Boolean} isDocument (true|false) default false
26592  * @cfg {String} url action url
26593  * @cfg {String} paramName default 'imageUpload'
26594  * @cfg {String} method default POST
26595  * @cfg {Boolean} loadMask (true|false) default true
26596  * @cfg {Boolean} loadingText default 'Loading...'
26597  * 
26598  * @constructor
26599  * Create a new UploadCropbox
26600  * @param {Object} config The config object
26601  */
26602
26603 Roo.bootstrap.UploadCropbox = function(config){
26604     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26605     
26606     this.addEvents({
26607         /**
26608          * @event beforeselectfile
26609          * Fire before select file
26610          * @param {Roo.bootstrap.UploadCropbox} this
26611          */
26612         "beforeselectfile" : true,
26613         /**
26614          * @event initial
26615          * Fire after initEvent
26616          * @param {Roo.bootstrap.UploadCropbox} this
26617          */
26618         "initial" : true,
26619         /**
26620          * @event crop
26621          * Fire after initEvent
26622          * @param {Roo.bootstrap.UploadCropbox} this
26623          * @param {String} data
26624          */
26625         "crop" : true,
26626         /**
26627          * @event prepare
26628          * Fire when preparing the file data
26629          * @param {Roo.bootstrap.UploadCropbox} this
26630          * @param {Object} file
26631          */
26632         "prepare" : true,
26633         /**
26634          * @event exception
26635          * Fire when get exception
26636          * @param {Roo.bootstrap.UploadCropbox} this
26637          * @param {XMLHttpRequest} xhr
26638          */
26639         "exception" : true,
26640         /**
26641          * @event beforeloadcanvas
26642          * Fire before load the canvas
26643          * @param {Roo.bootstrap.UploadCropbox} this
26644          * @param {String} src
26645          */
26646         "beforeloadcanvas" : true,
26647         /**
26648          * @event trash
26649          * Fire when trash image
26650          * @param {Roo.bootstrap.UploadCropbox} this
26651          */
26652         "trash" : true,
26653         /**
26654          * @event download
26655          * Fire when download the image
26656          * @param {Roo.bootstrap.UploadCropbox} this
26657          */
26658         "download" : true,
26659         /**
26660          * @event footerbuttonclick
26661          * Fire when footerbuttonclick
26662          * @param {Roo.bootstrap.UploadCropbox} this
26663          * @param {String} type
26664          */
26665         "footerbuttonclick" : true,
26666         /**
26667          * @event resize
26668          * Fire when resize
26669          * @param {Roo.bootstrap.UploadCropbox} this
26670          */
26671         "resize" : true,
26672         /**
26673          * @event rotate
26674          * Fire when rotate the image
26675          * @param {Roo.bootstrap.UploadCropbox} this
26676          * @param {String} pos
26677          */
26678         "rotate" : true,
26679         /**
26680          * @event inspect
26681          * Fire when inspect the file
26682          * @param {Roo.bootstrap.UploadCropbox} this
26683          * @param {Object} file
26684          */
26685         "inspect" : true,
26686         /**
26687          * @event upload
26688          * Fire when xhr upload the file
26689          * @param {Roo.bootstrap.UploadCropbox} this
26690          * @param {Object} data
26691          */
26692         "upload" : true,
26693         /**
26694          * @event arrange
26695          * Fire when arrange the file data
26696          * @param {Roo.bootstrap.UploadCropbox} this
26697          * @param {Object} formData
26698          */
26699         "arrange" : true
26700     });
26701     
26702     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26703 };
26704
26705 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26706     
26707     emptyText : 'Click to upload image',
26708     rotateNotify : 'Image is too small to rotate',
26709     errorTimeout : 3000,
26710     scale : 0,
26711     baseScale : 1,
26712     rotate : 0,
26713     dragable : false,
26714     pinching : false,
26715     mouseX : 0,
26716     mouseY : 0,
26717     cropData : false,
26718     minWidth : 300,
26719     minHeight : 300,
26720     file : false,
26721     exif : {},
26722     baseRotate : 1,
26723     cropType : 'image/jpeg',
26724     buttons : false,
26725     canvasLoaded : false,
26726     isDocument : false,
26727     method : 'POST',
26728     paramName : 'imageUpload',
26729     loadMask : true,
26730     loadingText : 'Loading...',
26731     maskEl : false,
26732     
26733     getAutoCreate : function()
26734     {
26735         var cfg = {
26736             tag : 'div',
26737             cls : 'roo-upload-cropbox',
26738             cn : [
26739                 {
26740                     tag : 'input',
26741                     cls : 'roo-upload-cropbox-selector',
26742                     type : 'file'
26743                 },
26744                 {
26745                     tag : 'div',
26746                     cls : 'roo-upload-cropbox-body',
26747                     style : 'cursor:pointer',
26748                     cn : [
26749                         {
26750                             tag : 'div',
26751                             cls : 'roo-upload-cropbox-preview'
26752                         },
26753                         {
26754                             tag : 'div',
26755                             cls : 'roo-upload-cropbox-thumb'
26756                         },
26757                         {
26758                             tag : 'div',
26759                             cls : 'roo-upload-cropbox-empty-notify',
26760                             html : this.emptyText
26761                         },
26762                         {
26763                             tag : 'div',
26764                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26765                             html : this.rotateNotify
26766                         }
26767                     ]
26768                 },
26769                 {
26770                     tag : 'div',
26771                     cls : 'roo-upload-cropbox-footer',
26772                     cn : {
26773                         tag : 'div',
26774                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26775                         cn : []
26776                     }
26777                 }
26778             ]
26779         };
26780         
26781         return cfg;
26782     },
26783     
26784     onRender : function(ct, position)
26785     {
26786         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26787         
26788         if (this.buttons.length) {
26789             
26790             Roo.each(this.buttons, function(bb) {
26791                 
26792                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26793                 
26794                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26795                 
26796             }, this);
26797         }
26798         
26799         if(this.loadMask){
26800             this.maskEl = this.el;
26801         }
26802     },
26803     
26804     initEvents : function()
26805     {
26806         this.urlAPI = (window.createObjectURL && window) || 
26807                                 (window.URL && URL.revokeObjectURL && URL) || 
26808                                 (window.webkitURL && webkitURL);
26809                         
26810         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26811         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26812         
26813         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26814         this.selectorEl.hide();
26815         
26816         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26817         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26818         
26819         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26820         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26821         this.thumbEl.hide();
26822         
26823         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26824         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26825         
26826         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26827         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26828         this.errorEl.hide();
26829         
26830         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26831         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26832         this.footerEl.hide();
26833         
26834         this.setThumbBoxSize();
26835         
26836         this.bind();
26837         
26838         this.resize();
26839         
26840         this.fireEvent('initial', this);
26841     },
26842
26843     bind : function()
26844     {
26845         var _this = this;
26846         
26847         window.addEventListener("resize", function() { _this.resize(); } );
26848         
26849         this.bodyEl.on('click', this.beforeSelectFile, this);
26850         
26851         if(Roo.isTouch){
26852             this.bodyEl.on('touchstart', this.onTouchStart, this);
26853             this.bodyEl.on('touchmove', this.onTouchMove, this);
26854             this.bodyEl.on('touchend', this.onTouchEnd, this);
26855         }
26856         
26857         if(!Roo.isTouch){
26858             this.bodyEl.on('mousedown', this.onMouseDown, this);
26859             this.bodyEl.on('mousemove', this.onMouseMove, this);
26860             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26861             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26862             Roo.get(document).on('mouseup', this.onMouseUp, this);
26863         }
26864         
26865         this.selectorEl.on('change', this.onFileSelected, this);
26866     },
26867     
26868     reset : function()
26869     {    
26870         this.scale = 0;
26871         this.baseScale = 1;
26872         this.rotate = 0;
26873         this.baseRotate = 1;
26874         this.dragable = false;
26875         this.pinching = false;
26876         this.mouseX = 0;
26877         this.mouseY = 0;
26878         this.cropData = false;
26879         this.notifyEl.dom.innerHTML = this.emptyText;
26880         
26881         this.selectorEl.dom.value = '';
26882         
26883     },
26884     
26885     resize : function()
26886     {
26887         if(this.fireEvent('resize', this) != false){
26888             this.setThumbBoxPosition();
26889             this.setCanvasPosition();
26890         }
26891     },
26892     
26893     onFooterButtonClick : function(e, el, o, type)
26894     {
26895         switch (type) {
26896             case 'rotate-left' :
26897                 this.onRotateLeft(e);
26898                 break;
26899             case 'rotate-right' :
26900                 this.onRotateRight(e);
26901                 break;
26902             case 'picture' :
26903                 this.beforeSelectFile(e);
26904                 break;
26905             case 'trash' :
26906                 this.trash(e);
26907                 break;
26908             case 'crop' :
26909                 this.crop(e);
26910                 break;
26911             case 'download' :
26912                 this.download(e);
26913                 break;
26914             default :
26915                 break;
26916         }
26917         
26918         this.fireEvent('footerbuttonclick', this, type);
26919     },
26920     
26921     beforeSelectFile : function(e)
26922     {
26923         e.preventDefault();
26924         
26925         if(this.fireEvent('beforeselectfile', this) != false){
26926             this.selectorEl.dom.click();
26927         }
26928     },
26929     
26930     onFileSelected : function(e)
26931     {
26932         e.preventDefault();
26933         
26934         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26935             return;
26936         }
26937         
26938         var file = this.selectorEl.dom.files[0];
26939         
26940         if(this.fireEvent('inspect', this, file) != false){
26941             this.prepare(file);
26942         }
26943         
26944     },
26945     
26946     trash : function(e)
26947     {
26948         this.fireEvent('trash', this);
26949     },
26950     
26951     download : function(e)
26952     {
26953         this.fireEvent('download', this);
26954     },
26955     
26956     loadCanvas : function(src)
26957     {   
26958         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26959             
26960             this.reset();
26961             
26962             this.imageEl = document.createElement('img');
26963             
26964             var _this = this;
26965             
26966             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26967             
26968             this.imageEl.src = src;
26969         }
26970     },
26971     
26972     onLoadCanvas : function()
26973     {   
26974         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26975         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26976         
26977         this.bodyEl.un('click', this.beforeSelectFile, this);
26978         
26979         this.notifyEl.hide();
26980         this.thumbEl.show();
26981         this.footerEl.show();
26982         
26983         this.baseRotateLevel();
26984         
26985         if(this.isDocument){
26986             this.setThumbBoxSize();
26987         }
26988         
26989         this.setThumbBoxPosition();
26990         
26991         this.baseScaleLevel();
26992         
26993         this.draw();
26994         
26995         this.resize();
26996         
26997         this.canvasLoaded = true;
26998         
26999         if(this.loadMask){
27000             this.maskEl.unmask();
27001         }
27002         
27003     },
27004     
27005     setCanvasPosition : function()
27006     {   
27007         if(!this.canvasEl){
27008             return;
27009         }
27010         
27011         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27012         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27013         
27014         this.previewEl.setLeft(pw);
27015         this.previewEl.setTop(ph);
27016         
27017     },
27018     
27019     onMouseDown : function(e)
27020     {   
27021         e.stopEvent();
27022         
27023         this.dragable = true;
27024         this.pinching = false;
27025         
27026         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27027             this.dragable = false;
27028             return;
27029         }
27030         
27031         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27032         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27033         
27034     },
27035     
27036     onMouseMove : function(e)
27037     {   
27038         e.stopEvent();
27039         
27040         if(!this.canvasLoaded){
27041             return;
27042         }
27043         
27044         if (!this.dragable){
27045             return;
27046         }
27047         
27048         var minX = Math.ceil(this.thumbEl.getLeft(true));
27049         var minY = Math.ceil(this.thumbEl.getTop(true));
27050         
27051         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27052         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27053         
27054         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27055         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27056         
27057         x = x - this.mouseX;
27058         y = y - this.mouseY;
27059         
27060         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27061         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27062         
27063         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27064         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27065         
27066         this.previewEl.setLeft(bgX);
27067         this.previewEl.setTop(bgY);
27068         
27069         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27070         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27071     },
27072     
27073     onMouseUp : function(e)
27074     {   
27075         e.stopEvent();
27076         
27077         this.dragable = false;
27078     },
27079     
27080     onMouseWheel : function(e)
27081     {   
27082         e.stopEvent();
27083         
27084         this.startScale = this.scale;
27085         
27086         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27087         
27088         if(!this.zoomable()){
27089             this.scale = this.startScale;
27090             return;
27091         }
27092         
27093         this.draw();
27094         
27095         return;
27096     },
27097     
27098     zoomable : function()
27099     {
27100         var minScale = this.thumbEl.getWidth() / this.minWidth;
27101         
27102         if(this.minWidth < this.minHeight){
27103             minScale = this.thumbEl.getHeight() / this.minHeight;
27104         }
27105         
27106         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27107         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27108         
27109         if(
27110                 this.isDocument &&
27111                 (this.rotate == 0 || this.rotate == 180) && 
27112                 (
27113                     width > this.imageEl.OriginWidth || 
27114                     height > this.imageEl.OriginHeight ||
27115                     (width < this.minWidth && height < this.minHeight)
27116                 )
27117         ){
27118             return false;
27119         }
27120         
27121         if(
27122                 this.isDocument &&
27123                 (this.rotate == 90 || this.rotate == 270) && 
27124                 (
27125                     width > this.imageEl.OriginWidth || 
27126                     height > this.imageEl.OriginHeight ||
27127                     (width < this.minHeight && height < this.minWidth)
27128                 )
27129         ){
27130             return false;
27131         }
27132         
27133         if(
27134                 !this.isDocument &&
27135                 (this.rotate == 0 || this.rotate == 180) && 
27136                 (
27137                     width < this.minWidth || 
27138                     width > this.imageEl.OriginWidth || 
27139                     height < this.minHeight || 
27140                     height > this.imageEl.OriginHeight
27141                 )
27142         ){
27143             return false;
27144         }
27145         
27146         if(
27147                 !this.isDocument &&
27148                 (this.rotate == 90 || this.rotate == 270) && 
27149                 (
27150                     width < this.minHeight || 
27151                     width > this.imageEl.OriginWidth || 
27152                     height < this.minWidth || 
27153                     height > this.imageEl.OriginHeight
27154                 )
27155         ){
27156             return false;
27157         }
27158         
27159         return true;
27160         
27161     },
27162     
27163     onRotateLeft : function(e)
27164     {   
27165         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27166             
27167             var minScale = this.thumbEl.getWidth() / this.minWidth;
27168             
27169             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27170             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27171             
27172             this.startScale = this.scale;
27173             
27174             while (this.getScaleLevel() < minScale){
27175             
27176                 this.scale = this.scale + 1;
27177                 
27178                 if(!this.zoomable()){
27179                     break;
27180                 }
27181                 
27182                 if(
27183                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27184                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27185                 ){
27186                     continue;
27187                 }
27188                 
27189                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27190
27191                 this.draw();
27192                 
27193                 return;
27194             }
27195             
27196             this.scale = this.startScale;
27197             
27198             this.onRotateFail();
27199             
27200             return false;
27201         }
27202         
27203         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27204
27205         if(this.isDocument){
27206             this.setThumbBoxSize();
27207             this.setThumbBoxPosition();
27208             this.setCanvasPosition();
27209         }
27210         
27211         this.draw();
27212         
27213         this.fireEvent('rotate', this, 'left');
27214         
27215     },
27216     
27217     onRotateRight : function(e)
27218     {
27219         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27220             
27221             var minScale = this.thumbEl.getWidth() / this.minWidth;
27222         
27223             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27224             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27225             
27226             this.startScale = this.scale;
27227             
27228             while (this.getScaleLevel() < minScale){
27229             
27230                 this.scale = this.scale + 1;
27231                 
27232                 if(!this.zoomable()){
27233                     break;
27234                 }
27235                 
27236                 if(
27237                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27238                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27239                 ){
27240                     continue;
27241                 }
27242                 
27243                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27244
27245                 this.draw();
27246                 
27247                 return;
27248             }
27249             
27250             this.scale = this.startScale;
27251             
27252             this.onRotateFail();
27253             
27254             return false;
27255         }
27256         
27257         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27258
27259         if(this.isDocument){
27260             this.setThumbBoxSize();
27261             this.setThumbBoxPosition();
27262             this.setCanvasPosition();
27263         }
27264         
27265         this.draw();
27266         
27267         this.fireEvent('rotate', this, 'right');
27268     },
27269     
27270     onRotateFail : function()
27271     {
27272         this.errorEl.show(true);
27273         
27274         var _this = this;
27275         
27276         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27277     },
27278     
27279     draw : function()
27280     {
27281         this.previewEl.dom.innerHTML = '';
27282         
27283         var canvasEl = document.createElement("canvas");
27284         
27285         var contextEl = canvasEl.getContext("2d");
27286         
27287         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27288         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27289         var center = this.imageEl.OriginWidth / 2;
27290         
27291         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27292             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27293             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27294             center = this.imageEl.OriginHeight / 2;
27295         }
27296         
27297         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27298         
27299         contextEl.translate(center, center);
27300         contextEl.rotate(this.rotate * Math.PI / 180);
27301
27302         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27303         
27304         this.canvasEl = document.createElement("canvas");
27305         
27306         this.contextEl = this.canvasEl.getContext("2d");
27307         
27308         switch (this.rotate) {
27309             case 0 :
27310                 
27311                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27312                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27313                 
27314                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27315                 
27316                 break;
27317             case 90 : 
27318                 
27319                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27320                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27321                 
27322                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27323                     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);
27324                     break;
27325                 }
27326                 
27327                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27328                 
27329                 break;
27330             case 180 :
27331                 
27332                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27333                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27334                 
27335                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27336                     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);
27337                     break;
27338                 }
27339                 
27340                 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);
27341                 
27342                 break;
27343             case 270 :
27344                 
27345                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27346                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27347         
27348                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27349                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27350                     break;
27351                 }
27352                 
27353                 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);
27354                 
27355                 break;
27356             default : 
27357                 break;
27358         }
27359         
27360         this.previewEl.appendChild(this.canvasEl);
27361         
27362         this.setCanvasPosition();
27363     },
27364     
27365     crop : function()
27366     {
27367         if(!this.canvasLoaded){
27368             return;
27369         }
27370         
27371         var imageCanvas = document.createElement("canvas");
27372         
27373         var imageContext = imageCanvas.getContext("2d");
27374         
27375         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27376         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27377         
27378         var center = imageCanvas.width / 2;
27379         
27380         imageContext.translate(center, center);
27381         
27382         imageContext.rotate(this.rotate * Math.PI / 180);
27383         
27384         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27385         
27386         var canvas = document.createElement("canvas");
27387         
27388         var context = canvas.getContext("2d");
27389                 
27390         canvas.width = this.minWidth;
27391         canvas.height = this.minHeight;
27392
27393         switch (this.rotate) {
27394             case 0 :
27395                 
27396                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27397                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27398                 
27399                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27400                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27401                 
27402                 var targetWidth = this.minWidth - 2 * x;
27403                 var targetHeight = this.minHeight - 2 * y;
27404                 
27405                 var scale = 1;
27406                 
27407                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27408                     scale = targetWidth / width;
27409                 }
27410                 
27411                 if(x > 0 && y == 0){
27412                     scale = targetHeight / height;
27413                 }
27414                 
27415                 if(x > 0 && y > 0){
27416                     scale = targetWidth / width;
27417                     
27418                     if(width < height){
27419                         scale = targetHeight / height;
27420                     }
27421                 }
27422                 
27423                 context.scale(scale, scale);
27424                 
27425                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27426                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27427
27428                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27429                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27430
27431                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27432                 
27433                 break;
27434             case 90 : 
27435                 
27436                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27437                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27438                 
27439                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27440                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27441                 
27442                 var targetWidth = this.minWidth - 2 * x;
27443                 var targetHeight = this.minHeight - 2 * y;
27444                 
27445                 var scale = 1;
27446                 
27447                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27448                     scale = targetWidth / width;
27449                 }
27450                 
27451                 if(x > 0 && y == 0){
27452                     scale = targetHeight / height;
27453                 }
27454                 
27455                 if(x > 0 && y > 0){
27456                     scale = targetWidth / width;
27457                     
27458                     if(width < height){
27459                         scale = targetHeight / height;
27460                     }
27461                 }
27462                 
27463                 context.scale(scale, scale);
27464                 
27465                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27466                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27467
27468                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27469                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27470                 
27471                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27472                 
27473                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27474                 
27475                 break;
27476             case 180 :
27477                 
27478                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27479                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27480                 
27481                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27482                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27483                 
27484                 var targetWidth = this.minWidth - 2 * x;
27485                 var targetHeight = this.minHeight - 2 * y;
27486                 
27487                 var scale = 1;
27488                 
27489                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27490                     scale = targetWidth / width;
27491                 }
27492                 
27493                 if(x > 0 && y == 0){
27494                     scale = targetHeight / height;
27495                 }
27496                 
27497                 if(x > 0 && y > 0){
27498                     scale = targetWidth / width;
27499                     
27500                     if(width < height){
27501                         scale = targetHeight / height;
27502                     }
27503                 }
27504                 
27505                 context.scale(scale, scale);
27506                 
27507                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27508                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27509
27510                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27511                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27512
27513                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27514                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27515                 
27516                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27517                 
27518                 break;
27519             case 270 :
27520                 
27521                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27522                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27523                 
27524                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27525                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27526                 
27527                 var targetWidth = this.minWidth - 2 * x;
27528                 var targetHeight = this.minHeight - 2 * y;
27529                 
27530                 var scale = 1;
27531                 
27532                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27533                     scale = targetWidth / width;
27534                 }
27535                 
27536                 if(x > 0 && y == 0){
27537                     scale = targetHeight / height;
27538                 }
27539                 
27540                 if(x > 0 && y > 0){
27541                     scale = targetWidth / width;
27542                     
27543                     if(width < height){
27544                         scale = targetHeight / height;
27545                     }
27546                 }
27547                 
27548                 context.scale(scale, scale);
27549                 
27550                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27551                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27552
27553                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27554                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27555                 
27556                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27557                 
27558                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27559                 
27560                 break;
27561             default : 
27562                 break;
27563         }
27564         
27565         this.cropData = canvas.toDataURL(this.cropType);
27566         
27567         if(this.fireEvent('crop', this, this.cropData) !== false){
27568             this.process(this.file, this.cropData);
27569         }
27570         
27571         return;
27572         
27573     },
27574     
27575     setThumbBoxSize : function()
27576     {
27577         var width, height;
27578         
27579         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27580             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27581             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27582             
27583             this.minWidth = width;
27584             this.minHeight = height;
27585             
27586             if(this.rotate == 90 || this.rotate == 270){
27587                 this.minWidth = height;
27588                 this.minHeight = width;
27589             }
27590         }
27591         
27592         height = 300;
27593         width = Math.ceil(this.minWidth * height / this.minHeight);
27594         
27595         if(this.minWidth > this.minHeight){
27596             width = 300;
27597             height = Math.ceil(this.minHeight * width / this.minWidth);
27598         }
27599         
27600         this.thumbEl.setStyle({
27601             width : width + 'px',
27602             height : height + 'px'
27603         });
27604
27605         return;
27606             
27607     },
27608     
27609     setThumbBoxPosition : function()
27610     {
27611         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27612         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27613         
27614         this.thumbEl.setLeft(x);
27615         this.thumbEl.setTop(y);
27616         
27617     },
27618     
27619     baseRotateLevel : function()
27620     {
27621         this.baseRotate = 1;
27622         
27623         if(
27624                 typeof(this.exif) != 'undefined' &&
27625                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27626                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27627         ){
27628             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27629         }
27630         
27631         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27632         
27633     },
27634     
27635     baseScaleLevel : function()
27636     {
27637         var width, height;
27638         
27639         if(this.isDocument){
27640             
27641             if(this.baseRotate == 6 || this.baseRotate == 8){
27642             
27643                 height = this.thumbEl.getHeight();
27644                 this.baseScale = height / this.imageEl.OriginWidth;
27645
27646                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27647                     width = this.thumbEl.getWidth();
27648                     this.baseScale = width / this.imageEl.OriginHeight;
27649                 }
27650
27651                 return;
27652             }
27653
27654             height = this.thumbEl.getHeight();
27655             this.baseScale = height / this.imageEl.OriginHeight;
27656
27657             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27658                 width = this.thumbEl.getWidth();
27659                 this.baseScale = width / this.imageEl.OriginWidth;
27660             }
27661
27662             return;
27663         }
27664         
27665         if(this.baseRotate == 6 || this.baseRotate == 8){
27666             
27667             width = this.thumbEl.getHeight();
27668             this.baseScale = width / this.imageEl.OriginHeight;
27669             
27670             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27671                 height = this.thumbEl.getWidth();
27672                 this.baseScale = height / this.imageEl.OriginHeight;
27673             }
27674             
27675             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27676                 height = this.thumbEl.getWidth();
27677                 this.baseScale = height / this.imageEl.OriginHeight;
27678                 
27679                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27680                     width = this.thumbEl.getHeight();
27681                     this.baseScale = width / this.imageEl.OriginWidth;
27682                 }
27683             }
27684             
27685             return;
27686         }
27687         
27688         width = this.thumbEl.getWidth();
27689         this.baseScale = width / this.imageEl.OriginWidth;
27690         
27691         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27692             height = this.thumbEl.getHeight();
27693             this.baseScale = height / this.imageEl.OriginHeight;
27694         }
27695         
27696         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27697             
27698             height = this.thumbEl.getHeight();
27699             this.baseScale = height / this.imageEl.OriginHeight;
27700             
27701             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27702                 width = this.thumbEl.getWidth();
27703                 this.baseScale = width / this.imageEl.OriginWidth;
27704             }
27705             
27706         }
27707         
27708         return;
27709     },
27710     
27711     getScaleLevel : function()
27712     {
27713         return this.baseScale * Math.pow(1.1, this.scale);
27714     },
27715     
27716     onTouchStart : function(e)
27717     {
27718         if(!this.canvasLoaded){
27719             this.beforeSelectFile(e);
27720             return;
27721         }
27722         
27723         var touches = e.browserEvent.touches;
27724         
27725         if(!touches){
27726             return;
27727         }
27728         
27729         if(touches.length == 1){
27730             this.onMouseDown(e);
27731             return;
27732         }
27733         
27734         if(touches.length != 2){
27735             return;
27736         }
27737         
27738         var coords = [];
27739         
27740         for(var i = 0, finger; finger = touches[i]; i++){
27741             coords.push(finger.pageX, finger.pageY);
27742         }
27743         
27744         var x = Math.pow(coords[0] - coords[2], 2);
27745         var y = Math.pow(coords[1] - coords[3], 2);
27746         
27747         this.startDistance = Math.sqrt(x + y);
27748         
27749         this.startScale = this.scale;
27750         
27751         this.pinching = true;
27752         this.dragable = false;
27753         
27754     },
27755     
27756     onTouchMove : function(e)
27757     {
27758         if(!this.pinching && !this.dragable){
27759             return;
27760         }
27761         
27762         var touches = e.browserEvent.touches;
27763         
27764         if(!touches){
27765             return;
27766         }
27767         
27768         if(this.dragable){
27769             this.onMouseMove(e);
27770             return;
27771         }
27772         
27773         var coords = [];
27774         
27775         for(var i = 0, finger; finger = touches[i]; i++){
27776             coords.push(finger.pageX, finger.pageY);
27777         }
27778         
27779         var x = Math.pow(coords[0] - coords[2], 2);
27780         var y = Math.pow(coords[1] - coords[3], 2);
27781         
27782         this.endDistance = Math.sqrt(x + y);
27783         
27784         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27785         
27786         if(!this.zoomable()){
27787             this.scale = this.startScale;
27788             return;
27789         }
27790         
27791         this.draw();
27792         
27793     },
27794     
27795     onTouchEnd : function(e)
27796     {
27797         this.pinching = false;
27798         this.dragable = false;
27799         
27800     },
27801     
27802     process : function(file, crop)
27803     {
27804         if(this.loadMask){
27805             this.maskEl.mask(this.loadingText);
27806         }
27807         
27808         this.xhr = new XMLHttpRequest();
27809         
27810         file.xhr = this.xhr;
27811
27812         this.xhr.open(this.method, this.url, true);
27813         
27814         var headers = {
27815             "Accept": "application/json",
27816             "Cache-Control": "no-cache",
27817             "X-Requested-With": "XMLHttpRequest"
27818         };
27819         
27820         for (var headerName in headers) {
27821             var headerValue = headers[headerName];
27822             if (headerValue) {
27823                 this.xhr.setRequestHeader(headerName, headerValue);
27824             }
27825         }
27826         
27827         var _this = this;
27828         
27829         this.xhr.onload = function()
27830         {
27831             _this.xhrOnLoad(_this.xhr);
27832         }
27833         
27834         this.xhr.onerror = function()
27835         {
27836             _this.xhrOnError(_this.xhr);
27837         }
27838         
27839         var formData = new FormData();
27840
27841         formData.append('returnHTML', 'NO');
27842         
27843         if(crop){
27844             formData.append('crop', crop);
27845         }
27846         
27847         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27848             formData.append(this.paramName, file, file.name);
27849         }
27850         
27851         if(typeof(file.filename) != 'undefined'){
27852             formData.append('filename', file.filename);
27853         }
27854         
27855         if(typeof(file.mimetype) != 'undefined'){
27856             formData.append('mimetype', file.mimetype);
27857         }
27858         
27859         if(this.fireEvent('arrange', this, formData) != false){
27860             this.xhr.send(formData);
27861         };
27862     },
27863     
27864     xhrOnLoad : function(xhr)
27865     {
27866         if(this.loadMask){
27867             this.maskEl.unmask();
27868         }
27869         
27870         if (xhr.readyState !== 4) {
27871             this.fireEvent('exception', this, xhr);
27872             return;
27873         }
27874
27875         var response = Roo.decode(xhr.responseText);
27876         
27877         if(!response.success){
27878             this.fireEvent('exception', this, xhr);
27879             return;
27880         }
27881         
27882         var response = Roo.decode(xhr.responseText);
27883         
27884         this.fireEvent('upload', this, response);
27885         
27886     },
27887     
27888     xhrOnError : function()
27889     {
27890         if(this.loadMask){
27891             this.maskEl.unmask();
27892         }
27893         
27894         Roo.log('xhr on error');
27895         
27896         var response = Roo.decode(xhr.responseText);
27897           
27898         Roo.log(response);
27899         
27900     },
27901     
27902     prepare : function(file)
27903     {   
27904         if(this.loadMask){
27905             this.maskEl.mask(this.loadingText);
27906         }
27907         
27908         this.file = false;
27909         this.exif = {};
27910         
27911         if(typeof(file) === 'string'){
27912             this.loadCanvas(file);
27913             return;
27914         }
27915         
27916         if(!file || !this.urlAPI){
27917             return;
27918         }
27919         
27920         this.file = file;
27921         this.cropType = file.type;
27922         
27923         var _this = this;
27924         
27925         if(this.fireEvent('prepare', this, this.file) != false){
27926             
27927             var reader = new FileReader();
27928             
27929             reader.onload = function (e) {
27930                 if (e.target.error) {
27931                     Roo.log(e.target.error);
27932                     return;
27933                 }
27934                 
27935                 var buffer = e.target.result,
27936                     dataView = new DataView(buffer),
27937                     offset = 2,
27938                     maxOffset = dataView.byteLength - 4,
27939                     markerBytes,
27940                     markerLength;
27941                 
27942                 if (dataView.getUint16(0) === 0xffd8) {
27943                     while (offset < maxOffset) {
27944                         markerBytes = dataView.getUint16(offset);
27945                         
27946                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27947                             markerLength = dataView.getUint16(offset + 2) + 2;
27948                             if (offset + markerLength > dataView.byteLength) {
27949                                 Roo.log('Invalid meta data: Invalid segment size.');
27950                                 break;
27951                             }
27952                             
27953                             if(markerBytes == 0xffe1){
27954                                 _this.parseExifData(
27955                                     dataView,
27956                                     offset,
27957                                     markerLength
27958                                 );
27959                             }
27960                             
27961                             offset += markerLength;
27962                             
27963                             continue;
27964                         }
27965                         
27966                         break;
27967                     }
27968                     
27969                 }
27970                 
27971                 var url = _this.urlAPI.createObjectURL(_this.file);
27972                 
27973                 _this.loadCanvas(url);
27974                 
27975                 return;
27976             }
27977             
27978             reader.readAsArrayBuffer(this.file);
27979             
27980         }
27981         
27982     },
27983     
27984     parseExifData : function(dataView, offset, length)
27985     {
27986         var tiffOffset = offset + 10,
27987             littleEndian,
27988             dirOffset;
27989     
27990         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27991             // No Exif data, might be XMP data instead
27992             return;
27993         }
27994         
27995         // Check for the ASCII code for "Exif" (0x45786966):
27996         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27997             // No Exif data, might be XMP data instead
27998             return;
27999         }
28000         if (tiffOffset + 8 > dataView.byteLength) {
28001             Roo.log('Invalid Exif data: Invalid segment size.');
28002             return;
28003         }
28004         // Check for the two null bytes:
28005         if (dataView.getUint16(offset + 8) !== 0x0000) {
28006             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28007             return;
28008         }
28009         // Check the byte alignment:
28010         switch (dataView.getUint16(tiffOffset)) {
28011         case 0x4949:
28012             littleEndian = true;
28013             break;
28014         case 0x4D4D:
28015             littleEndian = false;
28016             break;
28017         default:
28018             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28019             return;
28020         }
28021         // Check for the TIFF tag marker (0x002A):
28022         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28023             Roo.log('Invalid Exif data: Missing TIFF marker.');
28024             return;
28025         }
28026         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28027         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28028         
28029         this.parseExifTags(
28030             dataView,
28031             tiffOffset,
28032             tiffOffset + dirOffset,
28033             littleEndian
28034         );
28035     },
28036     
28037     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28038     {
28039         var tagsNumber,
28040             dirEndOffset,
28041             i;
28042         if (dirOffset + 6 > dataView.byteLength) {
28043             Roo.log('Invalid Exif data: Invalid directory offset.');
28044             return;
28045         }
28046         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28047         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28048         if (dirEndOffset + 4 > dataView.byteLength) {
28049             Roo.log('Invalid Exif data: Invalid directory size.');
28050             return;
28051         }
28052         for (i = 0; i < tagsNumber; i += 1) {
28053             this.parseExifTag(
28054                 dataView,
28055                 tiffOffset,
28056                 dirOffset + 2 + 12 * i, // tag offset
28057                 littleEndian
28058             );
28059         }
28060         // Return the offset to the next directory:
28061         return dataView.getUint32(dirEndOffset, littleEndian);
28062     },
28063     
28064     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28065     {
28066         var tag = dataView.getUint16(offset, littleEndian);
28067         
28068         this.exif[tag] = this.getExifValue(
28069             dataView,
28070             tiffOffset,
28071             offset,
28072             dataView.getUint16(offset + 2, littleEndian), // tag type
28073             dataView.getUint32(offset + 4, littleEndian), // tag length
28074             littleEndian
28075         );
28076     },
28077     
28078     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28079     {
28080         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28081             tagSize,
28082             dataOffset,
28083             values,
28084             i,
28085             str,
28086             c;
28087     
28088         if (!tagType) {
28089             Roo.log('Invalid Exif data: Invalid tag type.');
28090             return;
28091         }
28092         
28093         tagSize = tagType.size * length;
28094         // Determine if the value is contained in the dataOffset bytes,
28095         // or if the value at the dataOffset is a pointer to the actual data:
28096         dataOffset = tagSize > 4 ?
28097                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28098         if (dataOffset + tagSize > dataView.byteLength) {
28099             Roo.log('Invalid Exif data: Invalid data offset.');
28100             return;
28101         }
28102         if (length === 1) {
28103             return tagType.getValue(dataView, dataOffset, littleEndian);
28104         }
28105         values = [];
28106         for (i = 0; i < length; i += 1) {
28107             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28108         }
28109         
28110         if (tagType.ascii) {
28111             str = '';
28112             // Concatenate the chars:
28113             for (i = 0; i < values.length; i += 1) {
28114                 c = values[i];
28115                 // Ignore the terminating NULL byte(s):
28116                 if (c === '\u0000') {
28117                     break;
28118                 }
28119                 str += c;
28120             }
28121             return str;
28122         }
28123         return values;
28124     }
28125     
28126 });
28127
28128 Roo.apply(Roo.bootstrap.UploadCropbox, {
28129     tags : {
28130         'Orientation': 0x0112
28131     },
28132     
28133     Orientation: {
28134             1: 0, //'top-left',
28135 //            2: 'top-right',
28136             3: 180, //'bottom-right',
28137 //            4: 'bottom-left',
28138 //            5: 'left-top',
28139             6: 90, //'right-top',
28140 //            7: 'right-bottom',
28141             8: 270 //'left-bottom'
28142     },
28143     
28144     exifTagTypes : {
28145         // byte, 8-bit unsigned int:
28146         1: {
28147             getValue: function (dataView, dataOffset) {
28148                 return dataView.getUint8(dataOffset);
28149             },
28150             size: 1
28151         },
28152         // ascii, 8-bit byte:
28153         2: {
28154             getValue: function (dataView, dataOffset) {
28155                 return String.fromCharCode(dataView.getUint8(dataOffset));
28156             },
28157             size: 1,
28158             ascii: true
28159         },
28160         // short, 16 bit int:
28161         3: {
28162             getValue: function (dataView, dataOffset, littleEndian) {
28163                 return dataView.getUint16(dataOffset, littleEndian);
28164             },
28165             size: 2
28166         },
28167         // long, 32 bit int:
28168         4: {
28169             getValue: function (dataView, dataOffset, littleEndian) {
28170                 return dataView.getUint32(dataOffset, littleEndian);
28171             },
28172             size: 4
28173         },
28174         // rational = two long values, first is numerator, second is denominator:
28175         5: {
28176             getValue: function (dataView, dataOffset, littleEndian) {
28177                 return dataView.getUint32(dataOffset, littleEndian) /
28178                     dataView.getUint32(dataOffset + 4, littleEndian);
28179             },
28180             size: 8
28181         },
28182         // slong, 32 bit signed int:
28183         9: {
28184             getValue: function (dataView, dataOffset, littleEndian) {
28185                 return dataView.getInt32(dataOffset, littleEndian);
28186             },
28187             size: 4
28188         },
28189         // srational, two slongs, first is numerator, second is denominator:
28190         10: {
28191             getValue: function (dataView, dataOffset, littleEndian) {
28192                 return dataView.getInt32(dataOffset, littleEndian) /
28193                     dataView.getInt32(dataOffset + 4, littleEndian);
28194             },
28195             size: 8
28196         }
28197     },
28198     
28199     footer : {
28200         STANDARD : [
28201             {
28202                 tag : 'div',
28203                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28204                 action : 'rotate-left',
28205                 cn : [
28206                     {
28207                         tag : 'button',
28208                         cls : 'btn btn-default',
28209                         html : '<i class="fa fa-undo"></i>'
28210                     }
28211                 ]
28212             },
28213             {
28214                 tag : 'div',
28215                 cls : 'btn-group roo-upload-cropbox-picture',
28216                 action : 'picture',
28217                 cn : [
28218                     {
28219                         tag : 'button',
28220                         cls : 'btn btn-default',
28221                         html : '<i class="fa fa-picture-o"></i>'
28222                     }
28223                 ]
28224             },
28225             {
28226                 tag : 'div',
28227                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28228                 action : 'rotate-right',
28229                 cn : [
28230                     {
28231                         tag : 'button',
28232                         cls : 'btn btn-default',
28233                         html : '<i class="fa fa-repeat"></i>'
28234                     }
28235                 ]
28236             }
28237         ],
28238         DOCUMENT : [
28239             {
28240                 tag : 'div',
28241                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28242                 action : 'rotate-left',
28243                 cn : [
28244                     {
28245                         tag : 'button',
28246                         cls : 'btn btn-default',
28247                         html : '<i class="fa fa-undo"></i>'
28248                     }
28249                 ]
28250             },
28251             {
28252                 tag : 'div',
28253                 cls : 'btn-group roo-upload-cropbox-download',
28254                 action : 'download',
28255                 cn : [
28256                     {
28257                         tag : 'button',
28258                         cls : 'btn btn-default',
28259                         html : '<i class="fa fa-download"></i>'
28260                     }
28261                 ]
28262             },
28263             {
28264                 tag : 'div',
28265                 cls : 'btn-group roo-upload-cropbox-crop',
28266                 action : 'crop',
28267                 cn : [
28268                     {
28269                         tag : 'button',
28270                         cls : 'btn btn-default',
28271                         html : '<i class="fa fa-crop"></i>'
28272                     }
28273                 ]
28274             },
28275             {
28276                 tag : 'div',
28277                 cls : 'btn-group roo-upload-cropbox-trash',
28278                 action : 'trash',
28279                 cn : [
28280                     {
28281                         tag : 'button',
28282                         cls : 'btn btn-default',
28283                         html : '<i class="fa fa-trash"></i>'
28284                     }
28285                 ]
28286             },
28287             {
28288                 tag : 'div',
28289                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28290                 action : 'rotate-right',
28291                 cn : [
28292                     {
28293                         tag : 'button',
28294                         cls : 'btn btn-default',
28295                         html : '<i class="fa fa-repeat"></i>'
28296                     }
28297                 ]
28298             }
28299         ],
28300         ROTATOR : [
28301             {
28302                 tag : 'div',
28303                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28304                 action : 'rotate-left',
28305                 cn : [
28306                     {
28307                         tag : 'button',
28308                         cls : 'btn btn-default',
28309                         html : '<i class="fa fa-undo"></i>'
28310                     }
28311                 ]
28312             },
28313             {
28314                 tag : 'div',
28315                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28316                 action : 'rotate-right',
28317                 cn : [
28318                     {
28319                         tag : 'button',
28320                         cls : 'btn btn-default',
28321                         html : '<i class="fa fa-repeat"></i>'
28322                     }
28323                 ]
28324             }
28325         ]
28326     }
28327 });
28328
28329 /*
28330 * Licence: LGPL
28331 */
28332
28333 /**
28334  * @class Roo.bootstrap.DocumentManager
28335  * @extends Roo.bootstrap.Component
28336  * Bootstrap DocumentManager class
28337  * @cfg {String} paramName default 'imageUpload'
28338  * @cfg {String} toolTipName default 'filename'
28339  * @cfg {String} method default POST
28340  * @cfg {String} url action url
28341  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28342  * @cfg {Boolean} multiple multiple upload default true
28343  * @cfg {Number} thumbSize default 300
28344  * @cfg {String} fieldLabel
28345  * @cfg {Number} labelWidth default 4
28346  * @cfg {String} labelAlign (left|top) default left
28347  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28348 * @cfg {Number} labellg set the width of label (1-12)
28349  * @cfg {Number} labelmd set the width of label (1-12)
28350  * @cfg {Number} labelsm set the width of label (1-12)
28351  * @cfg {Number} labelxs set the width of label (1-12)
28352  * 
28353  * @constructor
28354  * Create a new DocumentManager
28355  * @param {Object} config The config object
28356  */
28357
28358 Roo.bootstrap.DocumentManager = function(config){
28359     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28360     
28361     this.files = [];
28362     this.delegates = [];
28363     
28364     this.addEvents({
28365         /**
28366          * @event initial
28367          * Fire when initial the DocumentManager
28368          * @param {Roo.bootstrap.DocumentManager} this
28369          */
28370         "initial" : true,
28371         /**
28372          * @event inspect
28373          * inspect selected file
28374          * @param {Roo.bootstrap.DocumentManager} this
28375          * @param {File} file
28376          */
28377         "inspect" : true,
28378         /**
28379          * @event exception
28380          * Fire when xhr load exception
28381          * @param {Roo.bootstrap.DocumentManager} this
28382          * @param {XMLHttpRequest} xhr
28383          */
28384         "exception" : true,
28385         /**
28386          * @event afterupload
28387          * Fire when xhr load exception
28388          * @param {Roo.bootstrap.DocumentManager} this
28389          * @param {XMLHttpRequest} xhr
28390          */
28391         "afterupload" : true,
28392         /**
28393          * @event prepare
28394          * prepare the form data
28395          * @param {Roo.bootstrap.DocumentManager} this
28396          * @param {Object} formData
28397          */
28398         "prepare" : true,
28399         /**
28400          * @event remove
28401          * Fire when remove the file
28402          * @param {Roo.bootstrap.DocumentManager} this
28403          * @param {Object} file
28404          */
28405         "remove" : true,
28406         /**
28407          * @event refresh
28408          * Fire after refresh the file
28409          * @param {Roo.bootstrap.DocumentManager} this
28410          */
28411         "refresh" : true,
28412         /**
28413          * @event click
28414          * Fire after click the image
28415          * @param {Roo.bootstrap.DocumentManager} this
28416          * @param {Object} file
28417          */
28418         "click" : true,
28419         /**
28420          * @event edit
28421          * Fire when upload a image and editable set to true
28422          * @param {Roo.bootstrap.DocumentManager} this
28423          * @param {Object} file
28424          */
28425         "edit" : true,
28426         /**
28427          * @event beforeselectfile
28428          * Fire before select file
28429          * @param {Roo.bootstrap.DocumentManager} this
28430          */
28431         "beforeselectfile" : true,
28432         /**
28433          * @event process
28434          * Fire before process file
28435          * @param {Roo.bootstrap.DocumentManager} this
28436          * @param {Object} file
28437          */
28438         "process" : true,
28439         /**
28440          * @event previewrendered
28441          * Fire when preview rendered
28442          * @param {Roo.bootstrap.DocumentManager} this
28443          * @param {Object} file
28444          */
28445         "previewrendered" : true
28446         
28447     });
28448 };
28449
28450 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28451     
28452     boxes : 0,
28453     inputName : '',
28454     thumbSize : 300,
28455     multiple : true,
28456     files : false,
28457     method : 'POST',
28458     url : '',
28459     paramName : 'imageUpload',
28460     toolTipName : 'filename',
28461     fieldLabel : '',
28462     labelWidth : 4,
28463     labelAlign : 'left',
28464     editable : true,
28465     delegates : false,
28466     xhr : false, 
28467     
28468     labellg : 0,
28469     labelmd : 0,
28470     labelsm : 0,
28471     labelxs : 0,
28472     
28473     getAutoCreate : function()
28474     {   
28475         var managerWidget = {
28476             tag : 'div',
28477             cls : 'roo-document-manager',
28478             cn : [
28479                 {
28480                     tag : 'input',
28481                     cls : 'roo-document-manager-selector',
28482                     type : 'file'
28483                 },
28484                 {
28485                     tag : 'div',
28486                     cls : 'roo-document-manager-uploader',
28487                     cn : [
28488                         {
28489                             tag : 'div',
28490                             cls : 'roo-document-manager-upload-btn',
28491                             html : '<i class="fa fa-plus"></i>'
28492                         }
28493                     ]
28494                     
28495                 }
28496             ]
28497         };
28498         
28499         var content = [
28500             {
28501                 tag : 'div',
28502                 cls : 'column col-md-12',
28503                 cn : managerWidget
28504             }
28505         ];
28506         
28507         if(this.fieldLabel.length){
28508             
28509             content = [
28510                 {
28511                     tag : 'div',
28512                     cls : 'column col-md-12',
28513                     html : this.fieldLabel
28514                 },
28515                 {
28516                     tag : 'div',
28517                     cls : 'column col-md-12',
28518                     cn : managerWidget
28519                 }
28520             ];
28521
28522             if(this.labelAlign == 'left'){
28523                 content = [
28524                     {
28525                         tag : 'div',
28526                         cls : 'column',
28527                         html : this.fieldLabel
28528                     },
28529                     {
28530                         tag : 'div',
28531                         cls : 'column',
28532                         cn : managerWidget
28533                     }
28534                 ];
28535                 
28536                 if(this.labelWidth > 12){
28537                     content[0].style = "width: " + this.labelWidth + 'px';
28538                 }
28539
28540                 if(this.labelWidth < 13 && this.labelmd == 0){
28541                     this.labelmd = this.labelWidth;
28542                 }
28543
28544                 if(this.labellg > 0){
28545                     content[0].cls += ' col-lg-' + this.labellg;
28546                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28547                 }
28548
28549                 if(this.labelmd > 0){
28550                     content[0].cls += ' col-md-' + this.labelmd;
28551                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28552                 }
28553
28554                 if(this.labelsm > 0){
28555                     content[0].cls += ' col-sm-' + this.labelsm;
28556                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28557                 }
28558
28559                 if(this.labelxs > 0){
28560                     content[0].cls += ' col-xs-' + this.labelxs;
28561                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28562                 }
28563                 
28564             }
28565         }
28566         
28567         var cfg = {
28568             tag : 'div',
28569             cls : 'row clearfix',
28570             cn : content
28571         };
28572         
28573         return cfg;
28574         
28575     },
28576     
28577     initEvents : function()
28578     {
28579         this.managerEl = this.el.select('.roo-document-manager', true).first();
28580         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28581         
28582         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28583         this.selectorEl.hide();
28584         
28585         if(this.multiple){
28586             this.selectorEl.attr('multiple', 'multiple');
28587         }
28588         
28589         this.selectorEl.on('change', this.onFileSelected, this);
28590         
28591         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28592         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28593         
28594         this.uploader.on('click', this.onUploaderClick, this);
28595         
28596         this.renderProgressDialog();
28597         
28598         var _this = this;
28599         
28600         window.addEventListener("resize", function() { _this.refresh(); } );
28601         
28602         this.fireEvent('initial', this);
28603     },
28604     
28605     renderProgressDialog : function()
28606     {
28607         var _this = this;
28608         
28609         this.progressDialog = new Roo.bootstrap.Modal({
28610             cls : 'roo-document-manager-progress-dialog',
28611             allow_close : false,
28612             title : '',
28613             buttons : [
28614                 {
28615                     name  :'cancel',
28616                     weight : 'danger',
28617                     html : 'Cancel'
28618                 }
28619             ], 
28620             listeners : { 
28621                 btnclick : function() {
28622                     _this.uploadCancel();
28623                     this.hide();
28624                 }
28625             }
28626         });
28627          
28628         this.progressDialog.render(Roo.get(document.body));
28629          
28630         this.progress = new Roo.bootstrap.Progress({
28631             cls : 'roo-document-manager-progress',
28632             active : true,
28633             striped : true
28634         });
28635         
28636         this.progress.render(this.progressDialog.getChildContainer());
28637         
28638         this.progressBar = new Roo.bootstrap.ProgressBar({
28639             cls : 'roo-document-manager-progress-bar',
28640             aria_valuenow : 0,
28641             aria_valuemin : 0,
28642             aria_valuemax : 12,
28643             panel : 'success'
28644         });
28645         
28646         this.progressBar.render(this.progress.getChildContainer());
28647     },
28648     
28649     onUploaderClick : function(e)
28650     {
28651         e.preventDefault();
28652      
28653         if(this.fireEvent('beforeselectfile', this) != false){
28654             this.selectorEl.dom.click();
28655         }
28656         
28657     },
28658     
28659     onFileSelected : function(e)
28660     {
28661         e.preventDefault();
28662         
28663         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28664             return;
28665         }
28666         
28667         Roo.each(this.selectorEl.dom.files, function(file){
28668             if(this.fireEvent('inspect', this, file) != false){
28669                 this.files.push(file);
28670             }
28671         }, this);
28672         
28673         this.queue();
28674         
28675     },
28676     
28677     queue : function()
28678     {
28679         this.selectorEl.dom.value = '';
28680         
28681         if(!this.files || !this.files.length){
28682             return;
28683         }
28684         
28685         if(this.boxes > 0 && this.files.length > this.boxes){
28686             this.files = this.files.slice(0, this.boxes);
28687         }
28688         
28689         this.uploader.show();
28690         
28691         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28692             this.uploader.hide();
28693         }
28694         
28695         var _this = this;
28696         
28697         var files = [];
28698         
28699         var docs = [];
28700         
28701         Roo.each(this.files, function(file){
28702             
28703             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28704                 var f = this.renderPreview(file);
28705                 files.push(f);
28706                 return;
28707             }
28708             
28709             if(file.type.indexOf('image') != -1){
28710                 this.delegates.push(
28711                     (function(){
28712                         _this.process(file);
28713                     }).createDelegate(this)
28714                 );
28715         
28716                 return;
28717             }
28718             
28719             docs.push(
28720                 (function(){
28721                     _this.process(file);
28722                 }).createDelegate(this)
28723             );
28724             
28725         }, this);
28726         
28727         this.files = files;
28728         
28729         this.delegates = this.delegates.concat(docs);
28730         
28731         if(!this.delegates.length){
28732             this.refresh();
28733             return;
28734         }
28735         
28736         this.progressBar.aria_valuemax = this.delegates.length;
28737         
28738         this.arrange();
28739         
28740         return;
28741     },
28742     
28743     arrange : function()
28744     {
28745         if(!this.delegates.length){
28746             this.progressDialog.hide();
28747             this.refresh();
28748             return;
28749         }
28750         
28751         var delegate = this.delegates.shift();
28752         
28753         this.progressDialog.show();
28754         
28755         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28756         
28757         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28758         
28759         delegate();
28760     },
28761     
28762     refresh : function()
28763     {
28764         this.uploader.show();
28765         
28766         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28767             this.uploader.hide();
28768         }
28769         
28770         Roo.isTouch ? this.closable(false) : this.closable(true);
28771         
28772         this.fireEvent('refresh', this);
28773     },
28774     
28775     onRemove : function(e, el, o)
28776     {
28777         e.preventDefault();
28778         
28779         this.fireEvent('remove', this, o);
28780         
28781     },
28782     
28783     remove : function(o)
28784     {
28785         var files = [];
28786         
28787         Roo.each(this.files, function(file){
28788             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28789                 files.push(file);
28790                 return;
28791             }
28792
28793             o.target.remove();
28794
28795         }, this);
28796         
28797         this.files = files;
28798         
28799         this.refresh();
28800     },
28801     
28802     clear : function()
28803     {
28804         Roo.each(this.files, function(file){
28805             if(!file.target){
28806                 return;
28807             }
28808             
28809             file.target.remove();
28810
28811         }, this);
28812         
28813         this.files = [];
28814         
28815         this.refresh();
28816     },
28817     
28818     onClick : function(e, el, o)
28819     {
28820         e.preventDefault();
28821         
28822         this.fireEvent('click', this, o);
28823         
28824     },
28825     
28826     closable : function(closable)
28827     {
28828         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28829             
28830             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28831             
28832             if(closable){
28833                 el.show();
28834                 return;
28835             }
28836             
28837             el.hide();
28838             
28839         }, this);
28840     },
28841     
28842     xhrOnLoad : function(xhr)
28843     {
28844         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28845             el.remove();
28846         }, this);
28847         
28848         if (xhr.readyState !== 4) {
28849             this.arrange();
28850             this.fireEvent('exception', this, xhr);
28851             return;
28852         }
28853
28854         var response = Roo.decode(xhr.responseText);
28855         
28856         if(!response.success){
28857             this.arrange();
28858             this.fireEvent('exception', this, xhr);
28859             return;
28860         }
28861         
28862         var file = this.renderPreview(response.data);
28863         
28864         this.files.push(file);
28865         
28866         this.arrange();
28867         
28868         this.fireEvent('afterupload', this, xhr);
28869         
28870     },
28871     
28872     xhrOnError : function(xhr)
28873     {
28874         Roo.log('xhr on error');
28875         
28876         var response = Roo.decode(xhr.responseText);
28877           
28878         Roo.log(response);
28879         
28880         this.arrange();
28881     },
28882     
28883     process : function(file)
28884     {
28885         if(this.fireEvent('process', this, file) !== false){
28886             if(this.editable && file.type.indexOf('image') != -1){
28887                 this.fireEvent('edit', this, file);
28888                 return;
28889             }
28890
28891             this.uploadStart(file, false);
28892
28893             return;
28894         }
28895         
28896     },
28897     
28898     uploadStart : function(file, crop)
28899     {
28900         this.xhr = new XMLHttpRequest();
28901         
28902         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28903             this.arrange();
28904             return;
28905         }
28906         
28907         file.xhr = this.xhr;
28908             
28909         this.managerEl.createChild({
28910             tag : 'div',
28911             cls : 'roo-document-manager-loading',
28912             cn : [
28913                 {
28914                     tag : 'div',
28915                     tooltip : file.name,
28916                     cls : 'roo-document-manager-thumb',
28917                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28918                 }
28919             ]
28920
28921         });
28922
28923         this.xhr.open(this.method, this.url, true);
28924         
28925         var headers = {
28926             "Accept": "application/json",
28927             "Cache-Control": "no-cache",
28928             "X-Requested-With": "XMLHttpRequest"
28929         };
28930         
28931         for (var headerName in headers) {
28932             var headerValue = headers[headerName];
28933             if (headerValue) {
28934                 this.xhr.setRequestHeader(headerName, headerValue);
28935             }
28936         }
28937         
28938         var _this = this;
28939         
28940         this.xhr.onload = function()
28941         {
28942             _this.xhrOnLoad(_this.xhr);
28943         }
28944         
28945         this.xhr.onerror = function()
28946         {
28947             _this.xhrOnError(_this.xhr);
28948         }
28949         
28950         var formData = new FormData();
28951
28952         formData.append('returnHTML', 'NO');
28953         
28954         if(crop){
28955             formData.append('crop', crop);
28956         }
28957         
28958         formData.append(this.paramName, file, file.name);
28959         
28960         var options = {
28961             file : file, 
28962             manually : false
28963         };
28964         
28965         if(this.fireEvent('prepare', this, formData, options) != false){
28966             
28967             if(options.manually){
28968                 return;
28969             }
28970             
28971             this.xhr.send(formData);
28972             return;
28973         };
28974         
28975         this.uploadCancel();
28976     },
28977     
28978     uploadCancel : function()
28979     {
28980         if (this.xhr) {
28981             this.xhr.abort();
28982         }
28983         
28984         this.delegates = [];
28985         
28986         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28987             el.remove();
28988         }, this);
28989         
28990         this.arrange();
28991     },
28992     
28993     renderPreview : function(file)
28994     {
28995         if(typeof(file.target) != 'undefined' && file.target){
28996             return file;
28997         }
28998         
28999         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29000         
29001         var previewEl = this.managerEl.createChild({
29002             tag : 'div',
29003             cls : 'roo-document-manager-preview',
29004             cn : [
29005                 {
29006                     tag : 'div',
29007                     tooltip : file[this.toolTipName],
29008                     cls : 'roo-document-manager-thumb',
29009                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29010                 },
29011                 {
29012                     tag : 'button',
29013                     cls : 'close',
29014                     html : '<i class="fa fa-times-circle"></i>'
29015                 }
29016             ]
29017         });
29018
29019         var close = previewEl.select('button.close', true).first();
29020
29021         close.on('click', this.onRemove, this, file);
29022
29023         file.target = previewEl;
29024
29025         var image = previewEl.select('img', true).first();
29026         
29027         var _this = this;
29028         
29029         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29030         
29031         image.on('click', this.onClick, this, file);
29032         
29033         this.fireEvent('previewrendered', this, file);
29034         
29035         return file;
29036         
29037     },
29038     
29039     onPreviewLoad : function(file, image)
29040     {
29041         if(typeof(file.target) == 'undefined' || !file.target){
29042             return;
29043         }
29044         
29045         var width = image.dom.naturalWidth || image.dom.width;
29046         var height = image.dom.naturalHeight || image.dom.height;
29047         
29048         if(width > height){
29049             file.target.addClass('wide');
29050             return;
29051         }
29052         
29053         file.target.addClass('tall');
29054         return;
29055         
29056     },
29057     
29058     uploadFromSource : function(file, crop)
29059     {
29060         this.xhr = new XMLHttpRequest();
29061         
29062         this.managerEl.createChild({
29063             tag : 'div',
29064             cls : 'roo-document-manager-loading',
29065             cn : [
29066                 {
29067                     tag : 'div',
29068                     tooltip : file.name,
29069                     cls : 'roo-document-manager-thumb',
29070                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29071                 }
29072             ]
29073
29074         });
29075
29076         this.xhr.open(this.method, this.url, true);
29077         
29078         var headers = {
29079             "Accept": "application/json",
29080             "Cache-Control": "no-cache",
29081             "X-Requested-With": "XMLHttpRequest"
29082         };
29083         
29084         for (var headerName in headers) {
29085             var headerValue = headers[headerName];
29086             if (headerValue) {
29087                 this.xhr.setRequestHeader(headerName, headerValue);
29088             }
29089         }
29090         
29091         var _this = this;
29092         
29093         this.xhr.onload = function()
29094         {
29095             _this.xhrOnLoad(_this.xhr);
29096         }
29097         
29098         this.xhr.onerror = function()
29099         {
29100             _this.xhrOnError(_this.xhr);
29101         }
29102         
29103         var formData = new FormData();
29104
29105         formData.append('returnHTML', 'NO');
29106         
29107         formData.append('crop', crop);
29108         
29109         if(typeof(file.filename) != 'undefined'){
29110             formData.append('filename', file.filename);
29111         }
29112         
29113         if(typeof(file.mimetype) != 'undefined'){
29114             formData.append('mimetype', file.mimetype);
29115         }
29116         
29117         Roo.log(formData);
29118         
29119         if(this.fireEvent('prepare', this, formData) != false){
29120             this.xhr.send(formData);
29121         };
29122     }
29123 });
29124
29125 /*
29126 * Licence: LGPL
29127 */
29128
29129 /**
29130  * @class Roo.bootstrap.DocumentViewer
29131  * @extends Roo.bootstrap.Component
29132  * Bootstrap DocumentViewer class
29133  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29134  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29135  * 
29136  * @constructor
29137  * Create a new DocumentViewer
29138  * @param {Object} config The config object
29139  */
29140
29141 Roo.bootstrap.DocumentViewer = function(config){
29142     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29143     
29144     this.addEvents({
29145         /**
29146          * @event initial
29147          * Fire after initEvent
29148          * @param {Roo.bootstrap.DocumentViewer} this
29149          */
29150         "initial" : true,
29151         /**
29152          * @event click
29153          * Fire after click
29154          * @param {Roo.bootstrap.DocumentViewer} this
29155          */
29156         "click" : true,
29157         /**
29158          * @event download
29159          * Fire after download button
29160          * @param {Roo.bootstrap.DocumentViewer} this
29161          */
29162         "download" : true,
29163         /**
29164          * @event trash
29165          * Fire after trash button
29166          * @param {Roo.bootstrap.DocumentViewer} this
29167          */
29168         "trash" : true
29169         
29170     });
29171 };
29172
29173 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29174     
29175     showDownload : true,
29176     
29177     showTrash : true,
29178     
29179     getAutoCreate : function()
29180     {
29181         var cfg = {
29182             tag : 'div',
29183             cls : 'roo-document-viewer',
29184             cn : [
29185                 {
29186                     tag : 'div',
29187                     cls : 'roo-document-viewer-body',
29188                     cn : [
29189                         {
29190                             tag : 'div',
29191                             cls : 'roo-document-viewer-thumb',
29192                             cn : [
29193                                 {
29194                                     tag : 'img',
29195                                     cls : 'roo-document-viewer-image'
29196                                 }
29197                             ]
29198                         }
29199                     ]
29200                 },
29201                 {
29202                     tag : 'div',
29203                     cls : 'roo-document-viewer-footer',
29204                     cn : {
29205                         tag : 'div',
29206                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29207                         cn : [
29208                             {
29209                                 tag : 'div',
29210                                 cls : 'btn-group roo-document-viewer-download',
29211                                 cn : [
29212                                     {
29213                                         tag : 'button',
29214                                         cls : 'btn btn-default',
29215                                         html : '<i class="fa fa-download"></i>'
29216                                     }
29217                                 ]
29218                             },
29219                             {
29220                                 tag : 'div',
29221                                 cls : 'btn-group roo-document-viewer-trash',
29222                                 cn : [
29223                                     {
29224                                         tag : 'button',
29225                                         cls : 'btn btn-default',
29226                                         html : '<i class="fa fa-trash"></i>'
29227                                     }
29228                                 ]
29229                             }
29230                         ]
29231                     }
29232                 }
29233             ]
29234         };
29235         
29236         return cfg;
29237     },
29238     
29239     initEvents : function()
29240     {
29241         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29242         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29243         
29244         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29245         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29246         
29247         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29248         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29249         
29250         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29251         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29252         
29253         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29254         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29255         
29256         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29257         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29258         
29259         this.bodyEl.on('click', this.onClick, this);
29260         this.downloadBtn.on('click', this.onDownload, this);
29261         this.trashBtn.on('click', this.onTrash, this);
29262         
29263         this.downloadBtn.hide();
29264         this.trashBtn.hide();
29265         
29266         if(this.showDownload){
29267             this.downloadBtn.show();
29268         }
29269         
29270         if(this.showTrash){
29271             this.trashBtn.show();
29272         }
29273         
29274         if(!this.showDownload && !this.showTrash) {
29275             this.footerEl.hide();
29276         }
29277         
29278     },
29279     
29280     initial : function()
29281     {
29282         this.fireEvent('initial', this);
29283         
29284     },
29285     
29286     onClick : function(e)
29287     {
29288         e.preventDefault();
29289         
29290         this.fireEvent('click', this);
29291     },
29292     
29293     onDownload : function(e)
29294     {
29295         e.preventDefault();
29296         
29297         this.fireEvent('download', this);
29298     },
29299     
29300     onTrash : function(e)
29301     {
29302         e.preventDefault();
29303         
29304         this.fireEvent('trash', this);
29305     }
29306     
29307 });
29308 /*
29309  * - LGPL
29310  *
29311  * nav progress bar
29312  * 
29313  */
29314
29315 /**
29316  * @class Roo.bootstrap.NavProgressBar
29317  * @extends Roo.bootstrap.Component
29318  * Bootstrap NavProgressBar class
29319  * 
29320  * @constructor
29321  * Create a new nav progress bar
29322  * @param {Object} config The config object
29323  */
29324
29325 Roo.bootstrap.NavProgressBar = function(config){
29326     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29327
29328     this.bullets = this.bullets || [];
29329    
29330 //    Roo.bootstrap.NavProgressBar.register(this);
29331      this.addEvents({
29332         /**
29333              * @event changed
29334              * Fires when the active item changes
29335              * @param {Roo.bootstrap.NavProgressBar} this
29336              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29337              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29338          */
29339         'changed': true
29340      });
29341     
29342 };
29343
29344 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29345     
29346     bullets : [],
29347     barItems : [],
29348     
29349     getAutoCreate : function()
29350     {
29351         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29352         
29353         cfg = {
29354             tag : 'div',
29355             cls : 'roo-navigation-bar-group',
29356             cn : [
29357                 {
29358                     tag : 'div',
29359                     cls : 'roo-navigation-top-bar'
29360                 },
29361                 {
29362                     tag : 'div',
29363                     cls : 'roo-navigation-bullets-bar',
29364                     cn : [
29365                         {
29366                             tag : 'ul',
29367                             cls : 'roo-navigation-bar'
29368                         }
29369                     ]
29370                 },
29371                 
29372                 {
29373                     tag : 'div',
29374                     cls : 'roo-navigation-bottom-bar'
29375                 }
29376             ]
29377             
29378         };
29379         
29380         return cfg;
29381         
29382     },
29383     
29384     initEvents: function() 
29385     {
29386         
29387     },
29388     
29389     onRender : function(ct, position) 
29390     {
29391         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29392         
29393         if(this.bullets.length){
29394             Roo.each(this.bullets, function(b){
29395                this.addItem(b);
29396             }, this);
29397         }
29398         
29399         this.format();
29400         
29401     },
29402     
29403     addItem : function(cfg)
29404     {
29405         var item = new Roo.bootstrap.NavProgressItem(cfg);
29406         
29407         item.parentId = this.id;
29408         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29409         
29410         if(cfg.html){
29411             var top = new Roo.bootstrap.Element({
29412                 tag : 'div',
29413                 cls : 'roo-navigation-bar-text'
29414             });
29415             
29416             var bottom = new Roo.bootstrap.Element({
29417                 tag : 'div',
29418                 cls : 'roo-navigation-bar-text'
29419             });
29420             
29421             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29422             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29423             
29424             var topText = new Roo.bootstrap.Element({
29425                 tag : 'span',
29426                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29427             });
29428             
29429             var bottomText = new Roo.bootstrap.Element({
29430                 tag : 'span',
29431                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29432             });
29433             
29434             topText.onRender(top.el, null);
29435             bottomText.onRender(bottom.el, null);
29436             
29437             item.topEl = top;
29438             item.bottomEl = bottom;
29439         }
29440         
29441         this.barItems.push(item);
29442         
29443         return item;
29444     },
29445     
29446     getActive : function()
29447     {
29448         var active = false;
29449         
29450         Roo.each(this.barItems, function(v){
29451             
29452             if (!v.isActive()) {
29453                 return;
29454             }
29455             
29456             active = v;
29457             return false;
29458             
29459         });
29460         
29461         return active;
29462     },
29463     
29464     setActiveItem : function(item)
29465     {
29466         var prev = false;
29467         
29468         Roo.each(this.barItems, function(v){
29469             if (v.rid == item.rid) {
29470                 return ;
29471             }
29472             
29473             if (v.isActive()) {
29474                 v.setActive(false);
29475                 prev = v;
29476             }
29477         });
29478
29479         item.setActive(true);
29480         
29481         this.fireEvent('changed', this, item, prev);
29482     },
29483     
29484     getBarItem: function(rid)
29485     {
29486         var ret = false;
29487         
29488         Roo.each(this.barItems, function(e) {
29489             if (e.rid != rid) {
29490                 return;
29491             }
29492             
29493             ret =  e;
29494             return false;
29495         });
29496         
29497         return ret;
29498     },
29499     
29500     indexOfItem : function(item)
29501     {
29502         var index = false;
29503         
29504         Roo.each(this.barItems, function(v, i){
29505             
29506             if (v.rid != item.rid) {
29507                 return;
29508             }
29509             
29510             index = i;
29511             return false
29512         });
29513         
29514         return index;
29515     },
29516     
29517     setActiveNext : function()
29518     {
29519         var i = this.indexOfItem(this.getActive());
29520         
29521         if (i > this.barItems.length) {
29522             return;
29523         }
29524         
29525         this.setActiveItem(this.barItems[i+1]);
29526     },
29527     
29528     setActivePrev : function()
29529     {
29530         var i = this.indexOfItem(this.getActive());
29531         
29532         if (i  < 1) {
29533             return;
29534         }
29535         
29536         this.setActiveItem(this.barItems[i-1]);
29537     },
29538     
29539     format : function()
29540     {
29541         if(!this.barItems.length){
29542             return;
29543         }
29544      
29545         var width = 100 / this.barItems.length;
29546         
29547         Roo.each(this.barItems, function(i){
29548             i.el.setStyle('width', width + '%');
29549             i.topEl.el.setStyle('width', width + '%');
29550             i.bottomEl.el.setStyle('width', width + '%');
29551         }, this);
29552         
29553     }
29554     
29555 });
29556 /*
29557  * - LGPL
29558  *
29559  * Nav Progress Item
29560  * 
29561  */
29562
29563 /**
29564  * @class Roo.bootstrap.NavProgressItem
29565  * @extends Roo.bootstrap.Component
29566  * Bootstrap NavProgressItem class
29567  * @cfg {String} rid the reference id
29568  * @cfg {Boolean} active (true|false) Is item active default false
29569  * @cfg {Boolean} disabled (true|false) Is item active default false
29570  * @cfg {String} html
29571  * @cfg {String} position (top|bottom) text position default bottom
29572  * @cfg {String} icon show icon instead of number
29573  * 
29574  * @constructor
29575  * Create a new NavProgressItem
29576  * @param {Object} config The config object
29577  */
29578 Roo.bootstrap.NavProgressItem = function(config){
29579     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29580     this.addEvents({
29581         // raw events
29582         /**
29583          * @event click
29584          * The raw click event for the entire grid.
29585          * @param {Roo.bootstrap.NavProgressItem} this
29586          * @param {Roo.EventObject} e
29587          */
29588         "click" : true
29589     });
29590    
29591 };
29592
29593 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29594     
29595     rid : '',
29596     active : false,
29597     disabled : false,
29598     html : '',
29599     position : 'bottom',
29600     icon : false,
29601     
29602     getAutoCreate : function()
29603     {
29604         var iconCls = 'roo-navigation-bar-item-icon';
29605         
29606         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29607         
29608         var cfg = {
29609             tag: 'li',
29610             cls: 'roo-navigation-bar-item',
29611             cn : [
29612                 {
29613                     tag : 'i',
29614                     cls : iconCls
29615                 }
29616             ]
29617         };
29618         
29619         if(this.active){
29620             cfg.cls += ' active';
29621         }
29622         if(this.disabled){
29623             cfg.cls += ' disabled';
29624         }
29625         
29626         return cfg;
29627     },
29628     
29629     disable : function()
29630     {
29631         this.setDisabled(true);
29632     },
29633     
29634     enable : function()
29635     {
29636         this.setDisabled(false);
29637     },
29638     
29639     initEvents: function() 
29640     {
29641         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29642         
29643         this.iconEl.on('click', this.onClick, this);
29644     },
29645     
29646     onClick : function(e)
29647     {
29648         e.preventDefault();
29649         
29650         if(this.disabled){
29651             return;
29652         }
29653         
29654         if(this.fireEvent('click', this, e) === false){
29655             return;
29656         };
29657         
29658         this.parent().setActiveItem(this);
29659     },
29660     
29661     isActive: function () 
29662     {
29663         return this.active;
29664     },
29665     
29666     setActive : function(state)
29667     {
29668         if(this.active == state){
29669             return;
29670         }
29671         
29672         this.active = state;
29673         
29674         if (state) {
29675             this.el.addClass('active');
29676             return;
29677         }
29678         
29679         this.el.removeClass('active');
29680         
29681         return;
29682     },
29683     
29684     setDisabled : function(state)
29685     {
29686         if(this.disabled == state){
29687             return;
29688         }
29689         
29690         this.disabled = state;
29691         
29692         if (state) {
29693             this.el.addClass('disabled');
29694             return;
29695         }
29696         
29697         this.el.removeClass('disabled');
29698     },
29699     
29700     tooltipEl : function()
29701     {
29702         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29703     }
29704 });
29705  
29706
29707  /*
29708  * - LGPL
29709  *
29710  * FieldLabel
29711  * 
29712  */
29713
29714 /**
29715  * @class Roo.bootstrap.FieldLabel
29716  * @extends Roo.bootstrap.Component
29717  * Bootstrap FieldLabel class
29718  * @cfg {String} html contents of the element
29719  * @cfg {String} tag tag of the element default label
29720  * @cfg {String} cls class of the element
29721  * @cfg {String} target label target 
29722  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29723  * @cfg {String} invalidClass default "text-warning"
29724  * @cfg {String} validClass default "text-success"
29725  * @cfg {String} iconTooltip default "This field is required"
29726  * @cfg {String} indicatorpos (left|right) default left
29727  * 
29728  * @constructor
29729  * Create a new FieldLabel
29730  * @param {Object} config The config object
29731  */
29732
29733 Roo.bootstrap.FieldLabel = function(config){
29734     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29735     
29736     this.addEvents({
29737             /**
29738              * @event invalid
29739              * Fires after the field has been marked as invalid.
29740              * @param {Roo.form.FieldLabel} this
29741              * @param {String} msg The validation message
29742              */
29743             invalid : true,
29744             /**
29745              * @event valid
29746              * Fires after the field has been validated with no errors.
29747              * @param {Roo.form.FieldLabel} this
29748              */
29749             valid : true
29750         });
29751 };
29752
29753 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29754     
29755     tag: 'label',
29756     cls: '',
29757     html: '',
29758     target: '',
29759     allowBlank : true,
29760     invalidClass : 'has-warning',
29761     validClass : 'has-success',
29762     iconTooltip : 'This field is required',
29763     indicatorpos : 'left',
29764     
29765     getAutoCreate : function(){
29766         
29767         var cfg = {
29768             tag : this.tag,
29769             cls : 'roo-bootstrap-field-label ' + this.cls,
29770             for : this.target,
29771             cn : [
29772                 {
29773                     tag : 'i',
29774                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29775                     tooltip : this.iconTooltip
29776                 },
29777                 {
29778                     tag : 'span',
29779                     html : this.html
29780                 }
29781             ] 
29782         };
29783         
29784         if(this.indicatorpos == 'right'){
29785             var cfg = {
29786                 tag : this.tag,
29787                 cls : 'roo-bootstrap-field-label ' + this.cls,
29788                 for : this.target,
29789                 cn : [
29790                     {
29791                         tag : 'span',
29792                         html : this.html
29793                     },
29794                     {
29795                         tag : 'i',
29796                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29797                         tooltip : this.iconTooltip
29798                     }
29799                 ] 
29800             };
29801         }
29802         
29803         return cfg;
29804     },
29805     
29806     initEvents: function() 
29807     {
29808         Roo.bootstrap.Element.superclass.initEvents.call(this);
29809         
29810         this.indicator = this.indicatorEl();
29811         
29812         if(this.indicator){
29813             this.indicator.removeClass('visible');
29814             this.indicator.addClass('invisible');
29815         }
29816         
29817         Roo.bootstrap.FieldLabel.register(this);
29818     },
29819     
29820     indicatorEl : function()
29821     {
29822         var indicator = this.el.select('i.roo-required-indicator',true).first();
29823         
29824         if(!indicator){
29825             return false;
29826         }
29827         
29828         return indicator;
29829         
29830     },
29831     
29832     /**
29833      * Mark this field as valid
29834      */
29835     markValid : function()
29836     {
29837         if(this.indicator){
29838             this.indicator.removeClass('visible');
29839             this.indicator.addClass('invisible');
29840         }
29841         
29842         this.el.removeClass(this.invalidClass);
29843         
29844         this.el.addClass(this.validClass);
29845         
29846         this.fireEvent('valid', this);
29847     },
29848     
29849     /**
29850      * Mark this field as invalid
29851      * @param {String} msg The validation message
29852      */
29853     markInvalid : function(msg)
29854     {
29855         if(this.indicator){
29856             this.indicator.removeClass('invisible');
29857             this.indicator.addClass('visible');
29858         }
29859         
29860         this.el.removeClass(this.validClass);
29861         
29862         this.el.addClass(this.invalidClass);
29863         
29864         this.fireEvent('invalid', this, msg);
29865     }
29866     
29867    
29868 });
29869
29870 Roo.apply(Roo.bootstrap.FieldLabel, {
29871     
29872     groups: {},
29873     
29874      /**
29875     * register a FieldLabel Group
29876     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29877     */
29878     register : function(label)
29879     {
29880         if(this.groups.hasOwnProperty(label.target)){
29881             return;
29882         }
29883      
29884         this.groups[label.target] = label;
29885         
29886     },
29887     /**
29888     * fetch a FieldLabel Group based on the target
29889     * @param {string} target
29890     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29891     */
29892     get: function(target) {
29893         if (typeof(this.groups[target]) == 'undefined') {
29894             return false;
29895         }
29896         
29897         return this.groups[target] ;
29898     }
29899 });
29900
29901  
29902
29903  /*
29904  * - LGPL
29905  *
29906  * page DateSplitField.
29907  * 
29908  */
29909
29910
29911 /**
29912  * @class Roo.bootstrap.DateSplitField
29913  * @extends Roo.bootstrap.Component
29914  * Bootstrap DateSplitField class
29915  * @cfg {string} fieldLabel - the label associated
29916  * @cfg {Number} labelWidth set the width of label (0-12)
29917  * @cfg {String} labelAlign (top|left)
29918  * @cfg {Boolean} dayAllowBlank (true|false) default false
29919  * @cfg {Boolean} monthAllowBlank (true|false) default false
29920  * @cfg {Boolean} yearAllowBlank (true|false) default false
29921  * @cfg {string} dayPlaceholder 
29922  * @cfg {string} monthPlaceholder
29923  * @cfg {string} yearPlaceholder
29924  * @cfg {string} dayFormat default 'd'
29925  * @cfg {string} monthFormat default 'm'
29926  * @cfg {string} yearFormat default 'Y'
29927  * @cfg {Number} labellg set the width of label (1-12)
29928  * @cfg {Number} labelmd set the width of label (1-12)
29929  * @cfg {Number} labelsm set the width of label (1-12)
29930  * @cfg {Number} labelxs set the width of label (1-12)
29931
29932  *     
29933  * @constructor
29934  * Create a new DateSplitField
29935  * @param {Object} config The config object
29936  */
29937
29938 Roo.bootstrap.DateSplitField = function(config){
29939     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29940     
29941     this.addEvents({
29942         // raw events
29943          /**
29944          * @event years
29945          * getting the data of years
29946          * @param {Roo.bootstrap.DateSplitField} this
29947          * @param {Object} years
29948          */
29949         "years" : true,
29950         /**
29951          * @event days
29952          * getting the data of days
29953          * @param {Roo.bootstrap.DateSplitField} this
29954          * @param {Object} days
29955          */
29956         "days" : true,
29957         /**
29958          * @event invalid
29959          * Fires after the field has been marked as invalid.
29960          * @param {Roo.form.Field} this
29961          * @param {String} msg The validation message
29962          */
29963         invalid : true,
29964        /**
29965          * @event valid
29966          * Fires after the field has been validated with no errors.
29967          * @param {Roo.form.Field} this
29968          */
29969         valid : true
29970     });
29971 };
29972
29973 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29974     
29975     fieldLabel : '',
29976     labelAlign : 'top',
29977     labelWidth : 3,
29978     dayAllowBlank : false,
29979     monthAllowBlank : false,
29980     yearAllowBlank : false,
29981     dayPlaceholder : '',
29982     monthPlaceholder : '',
29983     yearPlaceholder : '',
29984     dayFormat : 'd',
29985     monthFormat : 'm',
29986     yearFormat : 'Y',
29987     isFormField : true,
29988     labellg : 0,
29989     labelmd : 0,
29990     labelsm : 0,
29991     labelxs : 0,
29992     
29993     getAutoCreate : function()
29994     {
29995         var cfg = {
29996             tag : 'div',
29997             cls : 'row roo-date-split-field-group',
29998             cn : [
29999                 {
30000                     tag : 'input',
30001                     type : 'hidden',
30002                     cls : 'form-hidden-field roo-date-split-field-group-value',
30003                     name : this.name
30004                 }
30005             ]
30006         };
30007         
30008         var labelCls = 'col-md-12';
30009         var contentCls = 'col-md-4';
30010         
30011         if(this.fieldLabel){
30012             
30013             var label = {
30014                 tag : 'div',
30015                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30016                 cn : [
30017                     {
30018                         tag : 'label',
30019                         html : this.fieldLabel
30020                     }
30021                 ]
30022             };
30023             
30024             if(this.labelAlign == 'left'){
30025             
30026                 if(this.labelWidth > 12){
30027                     label.style = "width: " + this.labelWidth + 'px';
30028                 }
30029
30030                 if(this.labelWidth < 13 && this.labelmd == 0){
30031                     this.labelmd = this.labelWidth;
30032                 }
30033
30034                 if(this.labellg > 0){
30035                     labelCls = ' col-lg-' + this.labellg;
30036                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30037                 }
30038
30039                 if(this.labelmd > 0){
30040                     labelCls = ' col-md-' + this.labelmd;
30041                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30042                 }
30043
30044                 if(this.labelsm > 0){
30045                     labelCls = ' col-sm-' + this.labelsm;
30046                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30047                 }
30048
30049                 if(this.labelxs > 0){
30050                     labelCls = ' col-xs-' + this.labelxs;
30051                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30052                 }
30053             }
30054             
30055             label.cls += ' ' + labelCls;
30056             
30057             cfg.cn.push(label);
30058         }
30059         
30060         Roo.each(['day', 'month', 'year'], function(t){
30061             cfg.cn.push({
30062                 tag : 'div',
30063                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30064             });
30065         }, this);
30066         
30067         return cfg;
30068     },
30069     
30070     inputEl: function ()
30071     {
30072         return this.el.select('.roo-date-split-field-group-value', true).first();
30073     },
30074     
30075     onRender : function(ct, position) 
30076     {
30077         var _this = this;
30078         
30079         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30080         
30081         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30082         
30083         this.dayField = new Roo.bootstrap.ComboBox({
30084             allowBlank : this.dayAllowBlank,
30085             alwaysQuery : true,
30086             displayField : 'value',
30087             editable : false,
30088             fieldLabel : '',
30089             forceSelection : true,
30090             mode : 'local',
30091             placeholder : this.dayPlaceholder,
30092             selectOnFocus : true,
30093             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30094             triggerAction : 'all',
30095             typeAhead : true,
30096             valueField : 'value',
30097             store : new Roo.data.SimpleStore({
30098                 data : (function() {    
30099                     var days = [];
30100                     _this.fireEvent('days', _this, days);
30101                     return days;
30102                 })(),
30103                 fields : [ 'value' ]
30104             }),
30105             listeners : {
30106                 select : function (_self, record, index)
30107                 {
30108                     _this.setValue(_this.getValue());
30109                 }
30110             }
30111         });
30112
30113         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30114         
30115         this.monthField = new Roo.bootstrap.MonthField({
30116             after : '<i class=\"fa fa-calendar\"></i>',
30117             allowBlank : this.monthAllowBlank,
30118             placeholder : this.monthPlaceholder,
30119             readOnly : true,
30120             listeners : {
30121                 render : function (_self)
30122                 {
30123                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30124                         e.preventDefault();
30125                         _self.focus();
30126                     });
30127                 },
30128                 select : function (_self, oldvalue, newvalue)
30129                 {
30130                     _this.setValue(_this.getValue());
30131                 }
30132             }
30133         });
30134         
30135         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30136         
30137         this.yearField = new Roo.bootstrap.ComboBox({
30138             allowBlank : this.yearAllowBlank,
30139             alwaysQuery : true,
30140             displayField : 'value',
30141             editable : false,
30142             fieldLabel : '',
30143             forceSelection : true,
30144             mode : 'local',
30145             placeholder : this.yearPlaceholder,
30146             selectOnFocus : true,
30147             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30148             triggerAction : 'all',
30149             typeAhead : true,
30150             valueField : 'value',
30151             store : new Roo.data.SimpleStore({
30152                 data : (function() {
30153                     var years = [];
30154                     _this.fireEvent('years', _this, years);
30155                     return years;
30156                 })(),
30157                 fields : [ 'value' ]
30158             }),
30159             listeners : {
30160                 select : function (_self, record, index)
30161                 {
30162                     _this.setValue(_this.getValue());
30163                 }
30164             }
30165         });
30166
30167         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30168     },
30169     
30170     setValue : function(v, format)
30171     {
30172         this.inputEl.dom.value = v;
30173         
30174         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30175         
30176         var d = Date.parseDate(v, f);
30177         
30178         if(!d){
30179             this.validate();
30180             return;
30181         }
30182         
30183         this.setDay(d.format(this.dayFormat));
30184         this.setMonth(d.format(this.monthFormat));
30185         this.setYear(d.format(this.yearFormat));
30186         
30187         this.validate();
30188         
30189         return;
30190     },
30191     
30192     setDay : function(v)
30193     {
30194         this.dayField.setValue(v);
30195         this.inputEl.dom.value = this.getValue();
30196         this.validate();
30197         return;
30198     },
30199     
30200     setMonth : function(v)
30201     {
30202         this.monthField.setValue(v, true);
30203         this.inputEl.dom.value = this.getValue();
30204         this.validate();
30205         return;
30206     },
30207     
30208     setYear : function(v)
30209     {
30210         this.yearField.setValue(v);
30211         this.inputEl.dom.value = this.getValue();
30212         this.validate();
30213         return;
30214     },
30215     
30216     getDay : function()
30217     {
30218         return this.dayField.getValue();
30219     },
30220     
30221     getMonth : function()
30222     {
30223         return this.monthField.getValue();
30224     },
30225     
30226     getYear : function()
30227     {
30228         return this.yearField.getValue();
30229     },
30230     
30231     getValue : function()
30232     {
30233         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30234         
30235         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30236         
30237         return date;
30238     },
30239     
30240     reset : function()
30241     {
30242         this.setDay('');
30243         this.setMonth('');
30244         this.setYear('');
30245         this.inputEl.dom.value = '';
30246         this.validate();
30247         return;
30248     },
30249     
30250     validate : function()
30251     {
30252         var d = this.dayField.validate();
30253         var m = this.monthField.validate();
30254         var y = this.yearField.validate();
30255         
30256         var valid = true;
30257         
30258         if(
30259                 (!this.dayAllowBlank && !d) ||
30260                 (!this.monthAllowBlank && !m) ||
30261                 (!this.yearAllowBlank && !y)
30262         ){
30263             valid = false;
30264         }
30265         
30266         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30267             return valid;
30268         }
30269         
30270         if(valid){
30271             this.markValid();
30272             return valid;
30273         }
30274         
30275         this.markInvalid();
30276         
30277         return valid;
30278     },
30279     
30280     markValid : function()
30281     {
30282         
30283         var label = this.el.select('label', true).first();
30284         var icon = this.el.select('i.fa-star', true).first();
30285
30286         if(label && icon){
30287             icon.remove();
30288         }
30289         
30290         this.fireEvent('valid', this);
30291     },
30292     
30293      /**
30294      * Mark this field as invalid
30295      * @param {String} msg The validation message
30296      */
30297     markInvalid : function(msg)
30298     {
30299         
30300         var label = this.el.select('label', true).first();
30301         var icon = this.el.select('i.fa-star', true).first();
30302
30303         if(label && !icon){
30304             this.el.select('.roo-date-split-field-label', true).createChild({
30305                 tag : 'i',
30306                 cls : 'text-danger fa fa-lg fa-star',
30307                 tooltip : 'This field is required',
30308                 style : 'margin-right:5px;'
30309             }, label, true);
30310         }
30311         
30312         this.fireEvent('invalid', this, msg);
30313     },
30314     
30315     clearInvalid : function()
30316     {
30317         var label = this.el.select('label', true).first();
30318         var icon = this.el.select('i.fa-star', true).first();
30319
30320         if(label && icon){
30321             icon.remove();
30322         }
30323         
30324         this.fireEvent('valid', this);
30325     },
30326     
30327     getName: function()
30328     {
30329         return this.name;
30330     }
30331     
30332 });
30333
30334  /**
30335  *
30336  * This is based on 
30337  * http://masonry.desandro.com
30338  *
30339  * The idea is to render all the bricks based on vertical width...
30340  *
30341  * The original code extends 'outlayer' - we might need to use that....
30342  * 
30343  */
30344
30345
30346 /**
30347  * @class Roo.bootstrap.LayoutMasonry
30348  * @extends Roo.bootstrap.Component
30349  * Bootstrap Layout Masonry class
30350  * 
30351  * @constructor
30352  * Create a new Element
30353  * @param {Object} config The config object
30354  */
30355
30356 Roo.bootstrap.LayoutMasonry = function(config){
30357     
30358     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30359     
30360     this.bricks = [];
30361     
30362     Roo.bootstrap.LayoutMasonry.register(this);
30363     
30364     this.addEvents({
30365         // raw events
30366         /**
30367          * @event layout
30368          * Fire after layout the items
30369          * @param {Roo.bootstrap.LayoutMasonry} this
30370          * @param {Roo.EventObject} e
30371          */
30372         "layout" : true
30373     });
30374     
30375 };
30376
30377 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30378     
30379     /**
30380      * @cfg {Boolean} isLayoutInstant = no animation?
30381      */   
30382     isLayoutInstant : false, // needed?
30383    
30384     /**
30385      * @cfg {Number} boxWidth  width of the columns
30386      */   
30387     boxWidth : 450,
30388     
30389       /**
30390      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30391      */   
30392     boxHeight : 0,
30393     
30394     /**
30395      * @cfg {Number} padWidth padding below box..
30396      */   
30397     padWidth : 10, 
30398     
30399     /**
30400      * @cfg {Number} gutter gutter width..
30401      */   
30402     gutter : 10,
30403     
30404      /**
30405      * @cfg {Number} maxCols maximum number of columns
30406      */   
30407     
30408     maxCols: 0,
30409     
30410     /**
30411      * @cfg {Boolean} isAutoInitial defalut true
30412      */   
30413     isAutoInitial : true, 
30414     
30415     containerWidth: 0,
30416     
30417     /**
30418      * @cfg {Boolean} isHorizontal defalut false
30419      */   
30420     isHorizontal : false, 
30421
30422     currentSize : null,
30423     
30424     tag: 'div',
30425     
30426     cls: '',
30427     
30428     bricks: null, //CompositeElement
30429     
30430     cols : 1,
30431     
30432     _isLayoutInited : false,
30433     
30434 //    isAlternative : false, // only use for vertical layout...
30435     
30436     /**
30437      * @cfg {Number} alternativePadWidth padding below box..
30438      */   
30439     alternativePadWidth : 50,
30440     
30441     selectedBrick : [],
30442     
30443     getAutoCreate : function(){
30444         
30445         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30446         
30447         var cfg = {
30448             tag: this.tag,
30449             cls: 'blog-masonary-wrapper ' + this.cls,
30450             cn : {
30451                 cls : 'mas-boxes masonary'
30452             }
30453         };
30454         
30455         return cfg;
30456     },
30457     
30458     getChildContainer: function( )
30459     {
30460         if (this.boxesEl) {
30461             return this.boxesEl;
30462         }
30463         
30464         this.boxesEl = this.el.select('.mas-boxes').first();
30465         
30466         return this.boxesEl;
30467     },
30468     
30469     
30470     initEvents : function()
30471     {
30472         var _this = this;
30473         
30474         if(this.isAutoInitial){
30475             Roo.log('hook children rendered');
30476             this.on('childrenrendered', function() {
30477                 Roo.log('children rendered');
30478                 _this.initial();
30479             } ,this);
30480         }
30481     },
30482     
30483     initial : function()
30484     {
30485         this.selectedBrick = [];
30486         
30487         this.currentSize = this.el.getBox(true);
30488         
30489         Roo.EventManager.onWindowResize(this.resize, this); 
30490
30491         if(!this.isAutoInitial){
30492             this.layout();
30493             return;
30494         }
30495         
30496         this.layout();
30497         
30498         return;
30499         //this.layout.defer(500,this);
30500         
30501     },
30502     
30503     resize : function()
30504     {
30505         var cs = this.el.getBox(true);
30506         
30507         if (
30508                 this.currentSize.width == cs.width && 
30509                 this.currentSize.x == cs.x && 
30510                 this.currentSize.height == cs.height && 
30511                 this.currentSize.y == cs.y 
30512         ) {
30513             Roo.log("no change in with or X or Y");
30514             return;
30515         }
30516         
30517         this.currentSize = cs;
30518         
30519         this.layout();
30520         
30521     },
30522     
30523     layout : function()
30524     {   
30525         this._resetLayout();
30526         
30527         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30528         
30529         this.layoutItems( isInstant );
30530       
30531         this._isLayoutInited = true;
30532         
30533         this.fireEvent('layout', this);
30534         
30535     },
30536     
30537     _resetLayout : function()
30538     {
30539         if(this.isHorizontal){
30540             this.horizontalMeasureColumns();
30541             return;
30542         }
30543         
30544         this.verticalMeasureColumns();
30545         
30546     },
30547     
30548     verticalMeasureColumns : function()
30549     {
30550         this.getContainerWidth();
30551         
30552 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30553 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30554 //            return;
30555 //        }
30556         
30557         var boxWidth = this.boxWidth + this.padWidth;
30558         
30559         if(this.containerWidth < this.boxWidth){
30560             boxWidth = this.containerWidth
30561         }
30562         
30563         var containerWidth = this.containerWidth;
30564         
30565         var cols = Math.floor(containerWidth / boxWidth);
30566         
30567         this.cols = Math.max( cols, 1 );
30568         
30569         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30570         
30571         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30572         
30573         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30574         
30575         this.colWidth = boxWidth + avail - this.padWidth;
30576         
30577         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30578         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30579     },
30580     
30581     horizontalMeasureColumns : function()
30582     {
30583         this.getContainerWidth();
30584         
30585         var boxWidth = this.boxWidth;
30586         
30587         if(this.containerWidth < boxWidth){
30588             boxWidth = this.containerWidth;
30589         }
30590         
30591         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30592         
30593         this.el.setHeight(boxWidth);
30594         
30595     },
30596     
30597     getContainerWidth : function()
30598     {
30599         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30600     },
30601     
30602     layoutItems : function( isInstant )
30603     {
30604         Roo.log(this.bricks);
30605         
30606         var items = Roo.apply([], this.bricks);
30607         
30608         if(this.isHorizontal){
30609             this._horizontalLayoutItems( items , isInstant );
30610             return;
30611         }
30612         
30613 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30614 //            this._verticalAlternativeLayoutItems( items , isInstant );
30615 //            return;
30616 //        }
30617         
30618         this._verticalLayoutItems( items , isInstant );
30619         
30620     },
30621     
30622     _verticalLayoutItems : function ( items , isInstant)
30623     {
30624         if ( !items || !items.length ) {
30625             return;
30626         }
30627         
30628         var standard = [
30629             ['xs', 'xs', 'xs', 'tall'],
30630             ['xs', 'xs', 'tall'],
30631             ['xs', 'xs', 'sm'],
30632             ['xs', 'xs', 'xs'],
30633             ['xs', 'tall'],
30634             ['xs', 'sm'],
30635             ['xs', 'xs'],
30636             ['xs'],
30637             
30638             ['sm', 'xs', 'xs'],
30639             ['sm', 'xs'],
30640             ['sm'],
30641             
30642             ['tall', 'xs', 'xs', 'xs'],
30643             ['tall', 'xs', 'xs'],
30644             ['tall', 'xs'],
30645             ['tall']
30646             
30647         ];
30648         
30649         var queue = [];
30650         
30651         var boxes = [];
30652         
30653         var box = [];
30654         
30655         Roo.each(items, function(item, k){
30656             
30657             switch (item.size) {
30658                 // these layouts take up a full box,
30659                 case 'md' :
30660                 case 'md-left' :
30661                 case 'md-right' :
30662                 case 'wide' :
30663                     
30664                     if(box.length){
30665                         boxes.push(box);
30666                         box = [];
30667                     }
30668                     
30669                     boxes.push([item]);
30670                     
30671                     break;
30672                     
30673                 case 'xs' :
30674                 case 'sm' :
30675                 case 'tall' :
30676                     
30677                     box.push(item);
30678                     
30679                     break;
30680                 default :
30681                     break;
30682                     
30683             }
30684             
30685         }, this);
30686         
30687         if(box.length){
30688             boxes.push(box);
30689             box = [];
30690         }
30691         
30692         var filterPattern = function(box, length)
30693         {
30694             if(!box.length){
30695                 return;
30696             }
30697             
30698             var match = false;
30699             
30700             var pattern = box.slice(0, length);
30701             
30702             var format = [];
30703             
30704             Roo.each(pattern, function(i){
30705                 format.push(i.size);
30706             }, this);
30707             
30708             Roo.each(standard, function(s){
30709                 
30710                 if(String(s) != String(format)){
30711                     return;
30712                 }
30713                 
30714                 match = true;
30715                 return false;
30716                 
30717             }, this);
30718             
30719             if(!match && length == 1){
30720                 return;
30721             }
30722             
30723             if(!match){
30724                 filterPattern(box, length - 1);
30725                 return;
30726             }
30727                 
30728             queue.push(pattern);
30729
30730             box = box.slice(length, box.length);
30731
30732             filterPattern(box, 4);
30733
30734             return;
30735             
30736         }
30737         
30738         Roo.each(boxes, function(box, k){
30739             
30740             if(!box.length){
30741                 return;
30742             }
30743             
30744             if(box.length == 1){
30745                 queue.push(box);
30746                 return;
30747             }
30748             
30749             filterPattern(box, 4);
30750             
30751         }, this);
30752         
30753         this._processVerticalLayoutQueue( queue, isInstant );
30754         
30755     },
30756     
30757 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30758 //    {
30759 //        if ( !items || !items.length ) {
30760 //            return;
30761 //        }
30762 //
30763 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30764 //        
30765 //    },
30766     
30767     _horizontalLayoutItems : function ( items , isInstant)
30768     {
30769         if ( !items || !items.length || items.length < 3) {
30770             return;
30771         }
30772         
30773         items.reverse();
30774         
30775         var eItems = items.slice(0, 3);
30776         
30777         items = items.slice(3, items.length);
30778         
30779         var standard = [
30780             ['xs', 'xs', 'xs', 'wide'],
30781             ['xs', 'xs', 'wide'],
30782             ['xs', 'xs', 'sm'],
30783             ['xs', 'xs', 'xs'],
30784             ['xs', 'wide'],
30785             ['xs', 'sm'],
30786             ['xs', 'xs'],
30787             ['xs'],
30788             
30789             ['sm', 'xs', 'xs'],
30790             ['sm', 'xs'],
30791             ['sm'],
30792             
30793             ['wide', 'xs', 'xs', 'xs'],
30794             ['wide', 'xs', 'xs'],
30795             ['wide', 'xs'],
30796             ['wide'],
30797             
30798             ['wide-thin']
30799         ];
30800         
30801         var queue = [];
30802         
30803         var boxes = [];
30804         
30805         var box = [];
30806         
30807         Roo.each(items, function(item, k){
30808             
30809             switch (item.size) {
30810                 case 'md' :
30811                 case 'md-left' :
30812                 case 'md-right' :
30813                 case 'tall' :
30814                     
30815                     if(box.length){
30816                         boxes.push(box);
30817                         box = [];
30818                     }
30819                     
30820                     boxes.push([item]);
30821                     
30822                     break;
30823                     
30824                 case 'xs' :
30825                 case 'sm' :
30826                 case 'wide' :
30827                 case 'wide-thin' :
30828                     
30829                     box.push(item);
30830                     
30831                     break;
30832                 default :
30833                     break;
30834                     
30835             }
30836             
30837         }, this);
30838         
30839         if(box.length){
30840             boxes.push(box);
30841             box = [];
30842         }
30843         
30844         var filterPattern = function(box, length)
30845         {
30846             if(!box.length){
30847                 return;
30848             }
30849             
30850             var match = false;
30851             
30852             var pattern = box.slice(0, length);
30853             
30854             var format = [];
30855             
30856             Roo.each(pattern, function(i){
30857                 format.push(i.size);
30858             }, this);
30859             
30860             Roo.each(standard, function(s){
30861                 
30862                 if(String(s) != String(format)){
30863                     return;
30864                 }
30865                 
30866                 match = true;
30867                 return false;
30868                 
30869             }, this);
30870             
30871             if(!match && length == 1){
30872                 return;
30873             }
30874             
30875             if(!match){
30876                 filterPattern(box, length - 1);
30877                 return;
30878             }
30879                 
30880             queue.push(pattern);
30881
30882             box = box.slice(length, box.length);
30883
30884             filterPattern(box, 4);
30885
30886             return;
30887             
30888         }
30889         
30890         Roo.each(boxes, function(box, k){
30891             
30892             if(!box.length){
30893                 return;
30894             }
30895             
30896             if(box.length == 1){
30897                 queue.push(box);
30898                 return;
30899             }
30900             
30901             filterPattern(box, 4);
30902             
30903         }, this);
30904         
30905         
30906         var prune = [];
30907         
30908         var pos = this.el.getBox(true);
30909         
30910         var minX = pos.x;
30911         
30912         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30913         
30914         var hit_end = false;
30915         
30916         Roo.each(queue, function(box){
30917             
30918             if(hit_end){
30919                 
30920                 Roo.each(box, function(b){
30921                 
30922                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30923                     b.el.hide();
30924
30925                 }, this);
30926
30927                 return;
30928             }
30929             
30930             var mx = 0;
30931             
30932             Roo.each(box, function(b){
30933                 
30934                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30935                 b.el.show();
30936
30937                 mx = Math.max(mx, b.x);
30938                 
30939             }, this);
30940             
30941             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30942             
30943             if(maxX < minX){
30944                 
30945                 Roo.each(box, function(b){
30946                 
30947                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30948                     b.el.hide();
30949                     
30950                 }, this);
30951                 
30952                 hit_end = true;
30953                 
30954                 return;
30955             }
30956             
30957             prune.push(box);
30958             
30959         }, this);
30960         
30961         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30962     },
30963     
30964     /** Sets position of item in DOM
30965     * @param {Element} item
30966     * @param {Number} x - horizontal position
30967     * @param {Number} y - vertical position
30968     * @param {Boolean} isInstant - disables transitions
30969     */
30970     _processVerticalLayoutQueue : function( queue, isInstant )
30971     {
30972         var pos = this.el.getBox(true);
30973         var x = pos.x;
30974         var y = pos.y;
30975         var maxY = [];
30976         
30977         for (var i = 0; i < this.cols; i++){
30978             maxY[i] = pos.y;
30979         }
30980         
30981         Roo.each(queue, function(box, k){
30982             
30983             var col = k % this.cols;
30984             
30985             Roo.each(box, function(b,kk){
30986                 
30987                 b.el.position('absolute');
30988                 
30989                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30990                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30991                 
30992                 if(b.size == 'md-left' || b.size == 'md-right'){
30993                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30994                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30995                 }
30996                 
30997                 b.el.setWidth(width);
30998                 b.el.setHeight(height);
30999                 // iframe?
31000                 b.el.select('iframe',true).setSize(width,height);
31001                 
31002             }, this);
31003             
31004             for (var i = 0; i < this.cols; i++){
31005                 
31006                 if(maxY[i] < maxY[col]){
31007                     col = i;
31008                     continue;
31009                 }
31010                 
31011                 col = Math.min(col, i);
31012                 
31013             }
31014             
31015             x = pos.x + col * (this.colWidth + this.padWidth);
31016             
31017             y = maxY[col];
31018             
31019             var positions = [];
31020             
31021             switch (box.length){
31022                 case 1 :
31023                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31024                     break;
31025                 case 2 :
31026                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31027                     break;
31028                 case 3 :
31029                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31030                     break;
31031                 case 4 :
31032                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31033                     break;
31034                 default :
31035                     break;
31036             }
31037             
31038             Roo.each(box, function(b,kk){
31039                 
31040                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31041                 
31042                 var sz = b.el.getSize();
31043                 
31044                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31045                 
31046             }, this);
31047             
31048         }, this);
31049         
31050         var mY = 0;
31051         
31052         for (var i = 0; i < this.cols; i++){
31053             mY = Math.max(mY, maxY[i]);
31054         }
31055         
31056         this.el.setHeight(mY - pos.y);
31057         
31058     },
31059     
31060 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31061 //    {
31062 //        var pos = this.el.getBox(true);
31063 //        var x = pos.x;
31064 //        var y = pos.y;
31065 //        var maxX = pos.right;
31066 //        
31067 //        var maxHeight = 0;
31068 //        
31069 //        Roo.each(items, function(item, k){
31070 //            
31071 //            var c = k % 2;
31072 //            
31073 //            item.el.position('absolute');
31074 //                
31075 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31076 //
31077 //            item.el.setWidth(width);
31078 //
31079 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31080 //
31081 //            item.el.setHeight(height);
31082 //            
31083 //            if(c == 0){
31084 //                item.el.setXY([x, y], isInstant ? false : true);
31085 //            } else {
31086 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31087 //            }
31088 //            
31089 //            y = y + height + this.alternativePadWidth;
31090 //            
31091 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31092 //            
31093 //        }, this);
31094 //        
31095 //        this.el.setHeight(maxHeight);
31096 //        
31097 //    },
31098     
31099     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31100     {
31101         var pos = this.el.getBox(true);
31102         
31103         var minX = pos.x;
31104         var minY = pos.y;
31105         
31106         var maxX = pos.right;
31107         
31108         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31109         
31110         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31111         
31112         Roo.each(queue, function(box, k){
31113             
31114             Roo.each(box, function(b, kk){
31115                 
31116                 b.el.position('absolute');
31117                 
31118                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31119                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31120                 
31121                 if(b.size == 'md-left' || b.size == 'md-right'){
31122                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31123                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31124                 }
31125                 
31126                 b.el.setWidth(width);
31127                 b.el.setHeight(height);
31128                 
31129             }, this);
31130             
31131             if(!box.length){
31132                 return;
31133             }
31134             
31135             var positions = [];
31136             
31137             switch (box.length){
31138                 case 1 :
31139                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31140                     break;
31141                 case 2 :
31142                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31143                     break;
31144                 case 3 :
31145                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31146                     break;
31147                 case 4 :
31148                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31149                     break;
31150                 default :
31151                     break;
31152             }
31153             
31154             Roo.each(box, function(b,kk){
31155                 
31156                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31157                 
31158                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31159                 
31160             }, this);
31161             
31162         }, this);
31163         
31164     },
31165     
31166     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31167     {
31168         Roo.each(eItems, function(b,k){
31169             
31170             b.size = (k == 0) ? 'sm' : 'xs';
31171             b.x = (k == 0) ? 2 : 1;
31172             b.y = (k == 0) ? 2 : 1;
31173             
31174             b.el.position('absolute');
31175             
31176             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31177                 
31178             b.el.setWidth(width);
31179             
31180             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31181             
31182             b.el.setHeight(height);
31183             
31184         }, this);
31185
31186         var positions = [];
31187         
31188         positions.push({
31189             x : maxX - this.unitWidth * 2 - this.gutter,
31190             y : minY
31191         });
31192         
31193         positions.push({
31194             x : maxX - this.unitWidth,
31195             y : minY + (this.unitWidth + this.gutter) * 2
31196         });
31197         
31198         positions.push({
31199             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31200             y : minY
31201         });
31202         
31203         Roo.each(eItems, function(b,k){
31204             
31205             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31206
31207         }, this);
31208         
31209     },
31210     
31211     getVerticalOneBoxColPositions : function(x, y, box)
31212     {
31213         var pos = [];
31214         
31215         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31216         
31217         if(box[0].size == 'md-left'){
31218             rand = 0;
31219         }
31220         
31221         if(box[0].size == 'md-right'){
31222             rand = 1;
31223         }
31224         
31225         pos.push({
31226             x : x + (this.unitWidth + this.gutter) * rand,
31227             y : y
31228         });
31229         
31230         return pos;
31231     },
31232     
31233     getVerticalTwoBoxColPositions : function(x, y, box)
31234     {
31235         var pos = [];
31236         
31237         if(box[0].size == 'xs'){
31238             
31239             pos.push({
31240                 x : x,
31241                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31242             });
31243
31244             pos.push({
31245                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31246                 y : y
31247             });
31248             
31249             return pos;
31250             
31251         }
31252         
31253         pos.push({
31254             x : x,
31255             y : y
31256         });
31257
31258         pos.push({
31259             x : x + (this.unitWidth + this.gutter) * 2,
31260             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31261         });
31262         
31263         return pos;
31264         
31265     },
31266     
31267     getVerticalThreeBoxColPositions : function(x, y, box)
31268     {
31269         var pos = [];
31270         
31271         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31272             
31273             pos.push({
31274                 x : x,
31275                 y : y
31276             });
31277
31278             pos.push({
31279                 x : x + (this.unitWidth + this.gutter) * 1,
31280                 y : y
31281             });
31282             
31283             pos.push({
31284                 x : x + (this.unitWidth + this.gutter) * 2,
31285                 y : y
31286             });
31287             
31288             return pos;
31289             
31290         }
31291         
31292         if(box[0].size == 'xs' && box[1].size == 'xs'){
31293             
31294             pos.push({
31295                 x : x,
31296                 y : y
31297             });
31298
31299             pos.push({
31300                 x : x,
31301                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31302             });
31303             
31304             pos.push({
31305                 x : x + (this.unitWidth + this.gutter) * 1,
31306                 y : y
31307             });
31308             
31309             return pos;
31310             
31311         }
31312         
31313         pos.push({
31314             x : x,
31315             y : y
31316         });
31317
31318         pos.push({
31319             x : x + (this.unitWidth + this.gutter) * 2,
31320             y : y
31321         });
31322
31323         pos.push({
31324             x : x + (this.unitWidth + this.gutter) * 2,
31325             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31326         });
31327             
31328         return pos;
31329         
31330     },
31331     
31332     getVerticalFourBoxColPositions : function(x, y, box)
31333     {
31334         var pos = [];
31335         
31336         if(box[0].size == 'xs'){
31337             
31338             pos.push({
31339                 x : x,
31340                 y : y
31341             });
31342
31343             pos.push({
31344                 x : x,
31345                 y : y + (this.unitHeight + this.gutter) * 1
31346             });
31347             
31348             pos.push({
31349                 x : x,
31350                 y : y + (this.unitHeight + this.gutter) * 2
31351             });
31352             
31353             pos.push({
31354                 x : x + (this.unitWidth + this.gutter) * 1,
31355                 y : y
31356             });
31357             
31358             return pos;
31359             
31360         }
31361         
31362         pos.push({
31363             x : x,
31364             y : y
31365         });
31366
31367         pos.push({
31368             x : x + (this.unitWidth + this.gutter) * 2,
31369             y : y
31370         });
31371
31372         pos.push({
31373             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31374             y : y + (this.unitHeight + this.gutter) * 1
31375         });
31376
31377         pos.push({
31378             x : x + (this.unitWidth + this.gutter) * 2,
31379             y : y + (this.unitWidth + this.gutter) * 2
31380         });
31381
31382         return pos;
31383         
31384     },
31385     
31386     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31387     {
31388         var pos = [];
31389         
31390         if(box[0].size == 'md-left'){
31391             pos.push({
31392                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31393                 y : minY
31394             });
31395             
31396             return pos;
31397         }
31398         
31399         if(box[0].size == 'md-right'){
31400             pos.push({
31401                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31402                 y : minY + (this.unitWidth + this.gutter) * 1
31403             });
31404             
31405             return pos;
31406         }
31407         
31408         var rand = Math.floor(Math.random() * (4 - box[0].y));
31409         
31410         pos.push({
31411             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31412             y : minY + (this.unitWidth + this.gutter) * rand
31413         });
31414         
31415         return pos;
31416         
31417     },
31418     
31419     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31420     {
31421         var pos = [];
31422         
31423         if(box[0].size == 'xs'){
31424             
31425             pos.push({
31426                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31427                 y : minY
31428             });
31429
31430             pos.push({
31431                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31432                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31433             });
31434             
31435             return pos;
31436             
31437         }
31438         
31439         pos.push({
31440             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31441             y : minY
31442         });
31443
31444         pos.push({
31445             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31446             y : minY + (this.unitWidth + this.gutter) * 2
31447         });
31448         
31449         return pos;
31450         
31451     },
31452     
31453     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31454     {
31455         var pos = [];
31456         
31457         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31458             
31459             pos.push({
31460                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31461                 y : minY
31462             });
31463
31464             pos.push({
31465                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31466                 y : minY + (this.unitWidth + this.gutter) * 1
31467             });
31468             
31469             pos.push({
31470                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31471                 y : minY + (this.unitWidth + this.gutter) * 2
31472             });
31473             
31474             return pos;
31475             
31476         }
31477         
31478         if(box[0].size == 'xs' && box[1].size == 'xs'){
31479             
31480             pos.push({
31481                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31482                 y : minY
31483             });
31484
31485             pos.push({
31486                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31487                 y : minY
31488             });
31489             
31490             pos.push({
31491                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31492                 y : minY + (this.unitWidth + this.gutter) * 1
31493             });
31494             
31495             return pos;
31496             
31497         }
31498         
31499         pos.push({
31500             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31501             y : minY
31502         });
31503
31504         pos.push({
31505             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31506             y : minY + (this.unitWidth + this.gutter) * 2
31507         });
31508
31509         pos.push({
31510             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31511             y : minY + (this.unitWidth + this.gutter) * 2
31512         });
31513             
31514         return pos;
31515         
31516     },
31517     
31518     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31519     {
31520         var pos = [];
31521         
31522         if(box[0].size == 'xs'){
31523             
31524             pos.push({
31525                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31526                 y : minY
31527             });
31528
31529             pos.push({
31530                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31531                 y : minY
31532             });
31533             
31534             pos.push({
31535                 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),
31536                 y : minY
31537             });
31538             
31539             pos.push({
31540                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31541                 y : minY + (this.unitWidth + this.gutter) * 1
31542             });
31543             
31544             return pos;
31545             
31546         }
31547         
31548         pos.push({
31549             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31550             y : minY
31551         });
31552         
31553         pos.push({
31554             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31555             y : minY + (this.unitWidth + this.gutter) * 2
31556         });
31557         
31558         pos.push({
31559             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31560             y : minY + (this.unitWidth + this.gutter) * 2
31561         });
31562         
31563         pos.push({
31564             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),
31565             y : minY + (this.unitWidth + this.gutter) * 2
31566         });
31567
31568         return pos;
31569         
31570     },
31571     
31572     /**
31573     * remove a Masonry Brick
31574     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31575     */
31576     removeBrick : function(brick_id)
31577     {
31578         if (!brick_id) {
31579             return;
31580         }
31581         
31582         for (var i = 0; i<this.bricks.length; i++) {
31583             if (this.bricks[i].id == brick_id) {
31584                 this.bricks.splice(i,1);
31585                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31586                 this.initial();
31587             }
31588         }
31589     },
31590     
31591     /**
31592     * adds a Masonry Brick
31593     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31594     */
31595     addBrick : function(cfg)
31596     {
31597         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31598         //this.register(cn);
31599         cn.parentId = this.id;
31600         cn.onRender(this.el, null);
31601         return cn;
31602     },
31603     
31604     /**
31605     * register a Masonry Brick
31606     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31607     */
31608     
31609     register : function(brick)
31610     {
31611         this.bricks.push(brick);
31612         brick.masonryId = this.id;
31613     },
31614     
31615     /**
31616     * clear all the Masonry Brick
31617     */
31618     clearAll : function()
31619     {
31620         this.bricks = [];
31621         //this.getChildContainer().dom.innerHTML = "";
31622         this.el.dom.innerHTML = '';
31623     },
31624     
31625     getSelected : function()
31626     {
31627         if (!this.selectedBrick) {
31628             return false;
31629         }
31630         
31631         return this.selectedBrick;
31632     }
31633 });
31634
31635 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31636     
31637     groups: {},
31638      /**
31639     * register a Masonry Layout
31640     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31641     */
31642     
31643     register : function(layout)
31644     {
31645         this.groups[layout.id] = layout;
31646     },
31647     /**
31648     * fetch a  Masonry Layout based on the masonry layout ID
31649     * @param {string} the masonry layout to add
31650     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31651     */
31652     
31653     get: function(layout_id) {
31654         if (typeof(this.groups[layout_id]) == 'undefined') {
31655             return false;
31656         }
31657         return this.groups[layout_id] ;
31658     }
31659     
31660     
31661     
31662 });
31663
31664  
31665
31666  /**
31667  *
31668  * This is based on 
31669  * http://masonry.desandro.com
31670  *
31671  * The idea is to render all the bricks based on vertical width...
31672  *
31673  * The original code extends 'outlayer' - we might need to use that....
31674  * 
31675  */
31676
31677
31678 /**
31679  * @class Roo.bootstrap.LayoutMasonryAuto
31680  * @extends Roo.bootstrap.Component
31681  * Bootstrap Layout Masonry class
31682  * 
31683  * @constructor
31684  * Create a new Element
31685  * @param {Object} config The config object
31686  */
31687
31688 Roo.bootstrap.LayoutMasonryAuto = function(config){
31689     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31690 };
31691
31692 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31693     
31694       /**
31695      * @cfg {Boolean} isFitWidth  - resize the width..
31696      */   
31697     isFitWidth : false,  // options..
31698     /**
31699      * @cfg {Boolean} isOriginLeft = left align?
31700      */   
31701     isOriginLeft : true,
31702     /**
31703      * @cfg {Boolean} isOriginTop = top align?
31704      */   
31705     isOriginTop : false,
31706     /**
31707      * @cfg {Boolean} isLayoutInstant = no animation?
31708      */   
31709     isLayoutInstant : false, // needed?
31710     /**
31711      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31712      */   
31713     isResizingContainer : true,
31714     /**
31715      * @cfg {Number} columnWidth  width of the columns 
31716      */   
31717     
31718     columnWidth : 0,
31719     
31720     /**
31721      * @cfg {Number} maxCols maximum number of columns
31722      */   
31723     
31724     maxCols: 0,
31725     /**
31726      * @cfg {Number} padHeight padding below box..
31727      */   
31728     
31729     padHeight : 10, 
31730     
31731     /**
31732      * @cfg {Boolean} isAutoInitial defalut true
31733      */   
31734     
31735     isAutoInitial : true, 
31736     
31737     // private?
31738     gutter : 0,
31739     
31740     containerWidth: 0,
31741     initialColumnWidth : 0,
31742     currentSize : null,
31743     
31744     colYs : null, // array.
31745     maxY : 0,
31746     padWidth: 10,
31747     
31748     
31749     tag: 'div',
31750     cls: '',
31751     bricks: null, //CompositeElement
31752     cols : 0, // array?
31753     // element : null, // wrapped now this.el
31754     _isLayoutInited : null, 
31755     
31756     
31757     getAutoCreate : function(){
31758         
31759         var cfg = {
31760             tag: this.tag,
31761             cls: 'blog-masonary-wrapper ' + this.cls,
31762             cn : {
31763                 cls : 'mas-boxes masonary'
31764             }
31765         };
31766         
31767         return cfg;
31768     },
31769     
31770     getChildContainer: function( )
31771     {
31772         if (this.boxesEl) {
31773             return this.boxesEl;
31774         }
31775         
31776         this.boxesEl = this.el.select('.mas-boxes').first();
31777         
31778         return this.boxesEl;
31779     },
31780     
31781     
31782     initEvents : function()
31783     {
31784         var _this = this;
31785         
31786         if(this.isAutoInitial){
31787             Roo.log('hook children rendered');
31788             this.on('childrenrendered', function() {
31789                 Roo.log('children rendered');
31790                 _this.initial();
31791             } ,this);
31792         }
31793         
31794     },
31795     
31796     initial : function()
31797     {
31798         this.reloadItems();
31799
31800         this.currentSize = this.el.getBox(true);
31801
31802         /// was window resize... - let's see if this works..
31803         Roo.EventManager.onWindowResize(this.resize, this); 
31804
31805         if(!this.isAutoInitial){
31806             this.layout();
31807             return;
31808         }
31809         
31810         this.layout.defer(500,this);
31811     },
31812     
31813     reloadItems: function()
31814     {
31815         this.bricks = this.el.select('.masonry-brick', true);
31816         
31817         this.bricks.each(function(b) {
31818             //Roo.log(b.getSize());
31819             if (!b.attr('originalwidth')) {
31820                 b.attr('originalwidth',  b.getSize().width);
31821             }
31822             
31823         });
31824         
31825         Roo.log(this.bricks.elements.length);
31826     },
31827     
31828     resize : function()
31829     {
31830         Roo.log('resize');
31831         var cs = this.el.getBox(true);
31832         
31833         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31834             Roo.log("no change in with or X");
31835             return;
31836         }
31837         this.currentSize = cs;
31838         this.layout();
31839     },
31840     
31841     layout : function()
31842     {
31843          Roo.log('layout');
31844         this._resetLayout();
31845         //this._manageStamps();
31846       
31847         // don't animate first layout
31848         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31849         this.layoutItems( isInstant );
31850       
31851         // flag for initalized
31852         this._isLayoutInited = true;
31853     },
31854     
31855     layoutItems : function( isInstant )
31856     {
31857         //var items = this._getItemsForLayout( this.items );
31858         // original code supports filtering layout items.. we just ignore it..
31859         
31860         this._layoutItems( this.bricks , isInstant );
31861       
31862         this._postLayout();
31863     },
31864     _layoutItems : function ( items , isInstant)
31865     {
31866        //this.fireEvent( 'layout', this, items );
31867     
31868
31869         if ( !items || !items.elements.length ) {
31870           // no items, emit event with empty array
31871             return;
31872         }
31873
31874         var queue = [];
31875         items.each(function(item) {
31876             Roo.log("layout item");
31877             Roo.log(item);
31878             // get x/y object from method
31879             var position = this._getItemLayoutPosition( item );
31880             // enqueue
31881             position.item = item;
31882             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31883             queue.push( position );
31884         }, this);
31885       
31886         this._processLayoutQueue( queue );
31887     },
31888     /** Sets position of item in DOM
31889     * @param {Element} item
31890     * @param {Number} x - horizontal position
31891     * @param {Number} y - vertical position
31892     * @param {Boolean} isInstant - disables transitions
31893     */
31894     _processLayoutQueue : function( queue )
31895     {
31896         for ( var i=0, len = queue.length; i < len; i++ ) {
31897             var obj = queue[i];
31898             obj.item.position('absolute');
31899             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31900         }
31901     },
31902       
31903     
31904     /**
31905     * Any logic you want to do after each layout,
31906     * i.e. size the container
31907     */
31908     _postLayout : function()
31909     {
31910         this.resizeContainer();
31911     },
31912     
31913     resizeContainer : function()
31914     {
31915         if ( !this.isResizingContainer ) {
31916             return;
31917         }
31918         var size = this._getContainerSize();
31919         if ( size ) {
31920             this.el.setSize(size.width,size.height);
31921             this.boxesEl.setSize(size.width,size.height);
31922         }
31923     },
31924     
31925     
31926     
31927     _resetLayout : function()
31928     {
31929         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31930         this.colWidth = this.el.getWidth();
31931         //this.gutter = this.el.getWidth(); 
31932         
31933         this.measureColumns();
31934
31935         // reset column Y
31936         var i = this.cols;
31937         this.colYs = [];
31938         while (i--) {
31939             this.colYs.push( 0 );
31940         }
31941     
31942         this.maxY = 0;
31943     },
31944
31945     measureColumns : function()
31946     {
31947         this.getContainerWidth();
31948       // if columnWidth is 0, default to outerWidth of first item
31949         if ( !this.columnWidth ) {
31950             var firstItem = this.bricks.first();
31951             Roo.log(firstItem);
31952             this.columnWidth  = this.containerWidth;
31953             if (firstItem && firstItem.attr('originalwidth') ) {
31954                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31955             }
31956             // columnWidth fall back to item of first element
31957             Roo.log("set column width?");
31958                         this.initialColumnWidth = this.columnWidth  ;
31959
31960             // if first elem has no width, default to size of container
31961             
31962         }
31963         
31964         
31965         if (this.initialColumnWidth) {
31966             this.columnWidth = this.initialColumnWidth;
31967         }
31968         
31969         
31970             
31971         // column width is fixed at the top - however if container width get's smaller we should
31972         // reduce it...
31973         
31974         // this bit calcs how man columns..
31975             
31976         var columnWidth = this.columnWidth += this.gutter;
31977       
31978         // calculate columns
31979         var containerWidth = this.containerWidth + this.gutter;
31980         
31981         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31982         // fix rounding errors, typically with gutters
31983         var excess = columnWidth - containerWidth % columnWidth;
31984         
31985         
31986         // if overshoot is less than a pixel, round up, otherwise floor it
31987         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31988         cols = Math[ mathMethod ]( cols );
31989         this.cols = Math.max( cols, 1 );
31990         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31991         
31992          // padding positioning..
31993         var totalColWidth = this.cols * this.columnWidth;
31994         var padavail = this.containerWidth - totalColWidth;
31995         // so for 2 columns - we need 3 'pads'
31996         
31997         var padNeeded = (1+this.cols) * this.padWidth;
31998         
31999         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32000         
32001         this.columnWidth += padExtra
32002         //this.padWidth = Math.floor(padavail /  ( this.cols));
32003         
32004         // adjust colum width so that padding is fixed??
32005         
32006         // we have 3 columns ... total = width * 3
32007         // we have X left over... that should be used by 
32008         
32009         //if (this.expandC) {
32010             
32011         //}
32012         
32013         
32014         
32015     },
32016     
32017     getContainerWidth : function()
32018     {
32019        /* // container is parent if fit width
32020         var container = this.isFitWidth ? this.element.parentNode : this.element;
32021         // check that this.size and size are there
32022         // IE8 triggers resize on body size change, so they might not be
32023         
32024         var size = getSize( container );  //FIXME
32025         this.containerWidth = size && size.innerWidth; //FIXME
32026         */
32027          
32028         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32029         
32030     },
32031     
32032     _getItemLayoutPosition : function( item )  // what is item?
32033     {
32034         // we resize the item to our columnWidth..
32035       
32036         item.setWidth(this.columnWidth);
32037         item.autoBoxAdjust  = false;
32038         
32039         var sz = item.getSize();
32040  
32041         // how many columns does this brick span
32042         var remainder = this.containerWidth % this.columnWidth;
32043         
32044         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32045         // round if off by 1 pixel, otherwise use ceil
32046         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32047         colSpan = Math.min( colSpan, this.cols );
32048         
32049         // normally this should be '1' as we dont' currently allow multi width columns..
32050         
32051         var colGroup = this._getColGroup( colSpan );
32052         // get the minimum Y value from the columns
32053         var minimumY = Math.min.apply( Math, colGroup );
32054         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32055         
32056         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32057          
32058         // position the brick
32059         var position = {
32060             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32061             y: this.currentSize.y + minimumY + this.padHeight
32062         };
32063         
32064         Roo.log(position);
32065         // apply setHeight to necessary columns
32066         var setHeight = minimumY + sz.height + this.padHeight;
32067         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32068         
32069         var setSpan = this.cols + 1 - colGroup.length;
32070         for ( var i = 0; i < setSpan; i++ ) {
32071           this.colYs[ shortColIndex + i ] = setHeight ;
32072         }
32073       
32074         return position;
32075     },
32076     
32077     /**
32078      * @param {Number} colSpan - number of columns the element spans
32079      * @returns {Array} colGroup
32080      */
32081     _getColGroup : function( colSpan )
32082     {
32083         if ( colSpan < 2 ) {
32084           // if brick spans only one column, use all the column Ys
32085           return this.colYs;
32086         }
32087       
32088         var colGroup = [];
32089         // how many different places could this brick fit horizontally
32090         var groupCount = this.cols + 1 - colSpan;
32091         // for each group potential horizontal position
32092         for ( var i = 0; i < groupCount; i++ ) {
32093           // make an array of colY values for that one group
32094           var groupColYs = this.colYs.slice( i, i + colSpan );
32095           // and get the max value of the array
32096           colGroup[i] = Math.max.apply( Math, groupColYs );
32097         }
32098         return colGroup;
32099     },
32100     /*
32101     _manageStamp : function( stamp )
32102     {
32103         var stampSize =  stamp.getSize();
32104         var offset = stamp.getBox();
32105         // get the columns that this stamp affects
32106         var firstX = this.isOriginLeft ? offset.x : offset.right;
32107         var lastX = firstX + stampSize.width;
32108         var firstCol = Math.floor( firstX / this.columnWidth );
32109         firstCol = Math.max( 0, firstCol );
32110         
32111         var lastCol = Math.floor( lastX / this.columnWidth );
32112         // lastCol should not go over if multiple of columnWidth #425
32113         lastCol -= lastX % this.columnWidth ? 0 : 1;
32114         lastCol = Math.min( this.cols - 1, lastCol );
32115         
32116         // set colYs to bottom of the stamp
32117         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32118             stampSize.height;
32119             
32120         for ( var i = firstCol; i <= lastCol; i++ ) {
32121           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32122         }
32123     },
32124     */
32125     
32126     _getContainerSize : function()
32127     {
32128         this.maxY = Math.max.apply( Math, this.colYs );
32129         var size = {
32130             height: this.maxY
32131         };
32132       
32133         if ( this.isFitWidth ) {
32134             size.width = this._getContainerFitWidth();
32135         }
32136       
32137         return size;
32138     },
32139     
32140     _getContainerFitWidth : function()
32141     {
32142         var unusedCols = 0;
32143         // count unused columns
32144         var i = this.cols;
32145         while ( --i ) {
32146           if ( this.colYs[i] !== 0 ) {
32147             break;
32148           }
32149           unusedCols++;
32150         }
32151         // fit container to columns that have been used
32152         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32153     },
32154     
32155     needsResizeLayout : function()
32156     {
32157         var previousWidth = this.containerWidth;
32158         this.getContainerWidth();
32159         return previousWidth !== this.containerWidth;
32160     }
32161  
32162 });
32163
32164  
32165
32166  /*
32167  * - LGPL
32168  *
32169  * element
32170  * 
32171  */
32172
32173 /**
32174  * @class Roo.bootstrap.MasonryBrick
32175  * @extends Roo.bootstrap.Component
32176  * Bootstrap MasonryBrick class
32177  * 
32178  * @constructor
32179  * Create a new MasonryBrick
32180  * @param {Object} config The config object
32181  */
32182
32183 Roo.bootstrap.MasonryBrick = function(config){
32184     
32185     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32186     
32187     Roo.bootstrap.MasonryBrick.register(this);
32188     
32189     this.addEvents({
32190         // raw events
32191         /**
32192          * @event click
32193          * When a MasonryBrick is clcik
32194          * @param {Roo.bootstrap.MasonryBrick} this
32195          * @param {Roo.EventObject} e
32196          */
32197         "click" : true
32198     });
32199 };
32200
32201 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32202     
32203     /**
32204      * @cfg {String} title
32205      */   
32206     title : '',
32207     /**
32208      * @cfg {String} html
32209      */   
32210     html : '',
32211     /**
32212      * @cfg {String} bgimage
32213      */   
32214     bgimage : '',
32215     /**
32216      * @cfg {String} videourl
32217      */   
32218     videourl : '',
32219     /**
32220      * @cfg {String} cls
32221      */   
32222     cls : '',
32223     /**
32224      * @cfg {String} href
32225      */   
32226     href : '',
32227     /**
32228      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32229      */   
32230     size : 'xs',
32231     
32232     /**
32233      * @cfg {String} placetitle (center|bottom)
32234      */   
32235     placetitle : '',
32236     
32237     /**
32238      * @cfg {Boolean} isFitContainer defalut true
32239      */   
32240     isFitContainer : true, 
32241     
32242     /**
32243      * @cfg {Boolean} preventDefault defalut false
32244      */   
32245     preventDefault : false, 
32246     
32247     /**
32248      * @cfg {Boolean} inverse defalut false
32249      */   
32250     maskInverse : false, 
32251     
32252     getAutoCreate : function()
32253     {
32254         if(!this.isFitContainer){
32255             return this.getSplitAutoCreate();
32256         }
32257         
32258         var cls = 'masonry-brick masonry-brick-full';
32259         
32260         if(this.href.length){
32261             cls += ' masonry-brick-link';
32262         }
32263         
32264         if(this.bgimage.length){
32265             cls += ' masonry-brick-image';
32266         }
32267         
32268         if(this.maskInverse){
32269             cls += ' mask-inverse';
32270         }
32271         
32272         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32273             cls += ' enable-mask';
32274         }
32275         
32276         if(this.size){
32277             cls += ' masonry-' + this.size + '-brick';
32278         }
32279         
32280         if(this.placetitle.length){
32281             
32282             switch (this.placetitle) {
32283                 case 'center' :
32284                     cls += ' masonry-center-title';
32285                     break;
32286                 case 'bottom' :
32287                     cls += ' masonry-bottom-title';
32288                     break;
32289                 default:
32290                     break;
32291             }
32292             
32293         } else {
32294             if(!this.html.length && !this.bgimage.length){
32295                 cls += ' masonry-center-title';
32296             }
32297
32298             if(!this.html.length && this.bgimage.length){
32299                 cls += ' masonry-bottom-title';
32300             }
32301         }
32302         
32303         if(this.cls){
32304             cls += ' ' + this.cls;
32305         }
32306         
32307         var cfg = {
32308             tag: (this.href.length) ? 'a' : 'div',
32309             cls: cls,
32310             cn: [
32311                 {
32312                     tag: 'div',
32313                     cls: 'masonry-brick-mask'
32314                 },
32315                 {
32316                     tag: 'div',
32317                     cls: 'masonry-brick-paragraph',
32318                     cn: []
32319                 }
32320             ]
32321         };
32322         
32323         if(this.href.length){
32324             cfg.href = this.href;
32325         }
32326         
32327         var cn = cfg.cn[1].cn;
32328         
32329         if(this.title.length){
32330             cn.push({
32331                 tag: 'h4',
32332                 cls: 'masonry-brick-title',
32333                 html: this.title
32334             });
32335         }
32336         
32337         if(this.html.length){
32338             cn.push({
32339                 tag: 'p',
32340                 cls: 'masonry-brick-text',
32341                 html: this.html
32342             });
32343         }
32344         
32345         if (!this.title.length && !this.html.length) {
32346             cfg.cn[1].cls += ' hide';
32347         }
32348         
32349         if(this.bgimage.length){
32350             cfg.cn.push({
32351                 tag: 'img',
32352                 cls: 'masonry-brick-image-view',
32353                 src: this.bgimage
32354             });
32355         }
32356         
32357         if(this.videourl.length){
32358             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32359             // youtube support only?
32360             cfg.cn.push({
32361                 tag: 'iframe',
32362                 cls: 'masonry-brick-image-view',
32363                 src: vurl,
32364                 frameborder : 0,
32365                 allowfullscreen : true
32366             });
32367         }
32368         
32369         return cfg;
32370         
32371     },
32372     
32373     getSplitAutoCreate : function()
32374     {
32375         var cls = 'masonry-brick masonry-brick-split';
32376         
32377         if(this.href.length){
32378             cls += ' masonry-brick-link';
32379         }
32380         
32381         if(this.bgimage.length){
32382             cls += ' masonry-brick-image';
32383         }
32384         
32385         if(this.size){
32386             cls += ' masonry-' + this.size + '-brick';
32387         }
32388         
32389         switch (this.placetitle) {
32390             case 'center' :
32391                 cls += ' masonry-center-title';
32392                 break;
32393             case 'bottom' :
32394                 cls += ' masonry-bottom-title';
32395                 break;
32396             default:
32397                 if(!this.bgimage.length){
32398                     cls += ' masonry-center-title';
32399                 }
32400
32401                 if(this.bgimage.length){
32402                     cls += ' masonry-bottom-title';
32403                 }
32404                 break;
32405         }
32406         
32407         if(this.cls){
32408             cls += ' ' + this.cls;
32409         }
32410         
32411         var cfg = {
32412             tag: (this.href.length) ? 'a' : 'div',
32413             cls: cls,
32414             cn: [
32415                 {
32416                     tag: 'div',
32417                     cls: 'masonry-brick-split-head',
32418                     cn: [
32419                         {
32420                             tag: 'div',
32421                             cls: 'masonry-brick-paragraph',
32422                             cn: []
32423                         }
32424                     ]
32425                 },
32426                 {
32427                     tag: 'div',
32428                     cls: 'masonry-brick-split-body',
32429                     cn: []
32430                 }
32431             ]
32432         };
32433         
32434         if(this.href.length){
32435             cfg.href = this.href;
32436         }
32437         
32438         if(this.title.length){
32439             cfg.cn[0].cn[0].cn.push({
32440                 tag: 'h4',
32441                 cls: 'masonry-brick-title',
32442                 html: this.title
32443             });
32444         }
32445         
32446         if(this.html.length){
32447             cfg.cn[1].cn.push({
32448                 tag: 'p',
32449                 cls: 'masonry-brick-text',
32450                 html: this.html
32451             });
32452         }
32453
32454         if(this.bgimage.length){
32455             cfg.cn[0].cn.push({
32456                 tag: 'img',
32457                 cls: 'masonry-brick-image-view',
32458                 src: this.bgimage
32459             });
32460         }
32461         
32462         if(this.videourl.length){
32463             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32464             // youtube support only?
32465             cfg.cn[0].cn.cn.push({
32466                 tag: 'iframe',
32467                 cls: 'masonry-brick-image-view',
32468                 src: vurl,
32469                 frameborder : 0,
32470                 allowfullscreen : true
32471             });
32472         }
32473         
32474         return cfg;
32475     },
32476     
32477     initEvents: function() 
32478     {
32479         switch (this.size) {
32480             case 'xs' :
32481                 this.x = 1;
32482                 this.y = 1;
32483                 break;
32484             case 'sm' :
32485                 this.x = 2;
32486                 this.y = 2;
32487                 break;
32488             case 'md' :
32489             case 'md-left' :
32490             case 'md-right' :
32491                 this.x = 3;
32492                 this.y = 3;
32493                 break;
32494             case 'tall' :
32495                 this.x = 2;
32496                 this.y = 3;
32497                 break;
32498             case 'wide' :
32499                 this.x = 3;
32500                 this.y = 2;
32501                 break;
32502             case 'wide-thin' :
32503                 this.x = 3;
32504                 this.y = 1;
32505                 break;
32506                         
32507             default :
32508                 break;
32509         }
32510         
32511         if(Roo.isTouch){
32512             this.el.on('touchstart', this.onTouchStart, this);
32513             this.el.on('touchmove', this.onTouchMove, this);
32514             this.el.on('touchend', this.onTouchEnd, this);
32515             this.el.on('contextmenu', this.onContextMenu, this);
32516         } else {
32517             this.el.on('mouseenter'  ,this.enter, this);
32518             this.el.on('mouseleave', this.leave, this);
32519             this.el.on('click', this.onClick, this);
32520         }
32521         
32522         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32523             this.parent().bricks.push(this);   
32524         }
32525         
32526     },
32527     
32528     onClick: function(e, el)
32529     {
32530         var time = this.endTimer - this.startTimer;
32531         // Roo.log(e.preventDefault());
32532         if(Roo.isTouch){
32533             if(time > 1000){
32534                 e.preventDefault();
32535                 return;
32536             }
32537         }
32538         
32539         if(!this.preventDefault){
32540             return;
32541         }
32542         
32543         e.preventDefault();
32544         
32545         if (this.activcClass != '') {
32546             this.selectBrick();
32547         }
32548         
32549         this.fireEvent('click', this);
32550     },
32551     
32552     enter: function(e, el)
32553     {
32554         e.preventDefault();
32555         
32556         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32557             return;
32558         }
32559         
32560         if(this.bgimage.length && this.html.length){
32561             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32562         }
32563     },
32564     
32565     leave: function(e, el)
32566     {
32567         e.preventDefault();
32568         
32569         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32570             return;
32571         }
32572         
32573         if(this.bgimage.length && this.html.length){
32574             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32575         }
32576     },
32577     
32578     onTouchStart: function(e, el)
32579     {
32580 //        e.preventDefault();
32581         
32582         this.touchmoved = false;
32583         
32584         if(!this.isFitContainer){
32585             return;
32586         }
32587         
32588         if(!this.bgimage.length || !this.html.length){
32589             return;
32590         }
32591         
32592         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32593         
32594         this.timer = new Date().getTime();
32595         
32596     },
32597     
32598     onTouchMove: function(e, el)
32599     {
32600         this.touchmoved = true;
32601     },
32602     
32603     onContextMenu : function(e,el)
32604     {
32605         e.preventDefault();
32606         e.stopPropagation();
32607         return false;
32608     },
32609     
32610     onTouchEnd: function(e, el)
32611     {
32612 //        e.preventDefault();
32613         
32614         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32615         
32616             this.leave(e,el);
32617             
32618             return;
32619         }
32620         
32621         if(!this.bgimage.length || !this.html.length){
32622             
32623             if(this.href.length){
32624                 window.location.href = this.href;
32625             }
32626             
32627             return;
32628         }
32629         
32630         if(!this.isFitContainer){
32631             return;
32632         }
32633         
32634         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32635         
32636         window.location.href = this.href;
32637     },
32638     
32639     //selection on single brick only
32640     selectBrick : function() {
32641         
32642         if (!this.parentId) {
32643             return;
32644         }
32645         
32646         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32647         var index = m.selectedBrick.indexOf(this.id);
32648         
32649         if ( index > -1) {
32650             m.selectedBrick.splice(index,1);
32651             this.el.removeClass(this.activeClass);
32652             return;
32653         }
32654         
32655         for(var i = 0; i < m.selectedBrick.length; i++) {
32656             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32657             b.el.removeClass(b.activeClass);
32658         }
32659         
32660         m.selectedBrick = [];
32661         
32662         m.selectedBrick.push(this.id);
32663         this.el.addClass(this.activeClass);
32664         return;
32665     }
32666     
32667 });
32668
32669 Roo.apply(Roo.bootstrap.MasonryBrick, {
32670     
32671     //groups: {},
32672     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32673      /**
32674     * register a Masonry Brick
32675     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32676     */
32677     
32678     register : function(brick)
32679     {
32680         //this.groups[brick.id] = brick;
32681         this.groups.add(brick.id, brick);
32682     },
32683     /**
32684     * fetch a  masonry brick based on the masonry brick ID
32685     * @param {string} the masonry brick to add
32686     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32687     */
32688     
32689     get: function(brick_id) 
32690     {
32691         // if (typeof(this.groups[brick_id]) == 'undefined') {
32692         //     return false;
32693         // }
32694         // return this.groups[brick_id] ;
32695         
32696         if(this.groups.key(brick_id)) {
32697             return this.groups.key(brick_id);
32698         }
32699         
32700         return false;
32701     }
32702     
32703     
32704     
32705 });
32706
32707  /*
32708  * - LGPL
32709  *
32710  * element
32711  * 
32712  */
32713
32714 /**
32715  * @class Roo.bootstrap.Brick
32716  * @extends Roo.bootstrap.Component
32717  * Bootstrap Brick class
32718  * 
32719  * @constructor
32720  * Create a new Brick
32721  * @param {Object} config The config object
32722  */
32723
32724 Roo.bootstrap.Brick = function(config){
32725     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32726     
32727     this.addEvents({
32728         // raw events
32729         /**
32730          * @event click
32731          * When a Brick is click
32732          * @param {Roo.bootstrap.Brick} this
32733          * @param {Roo.EventObject} e
32734          */
32735         "click" : true
32736     });
32737 };
32738
32739 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32740     
32741     /**
32742      * @cfg {String} title
32743      */   
32744     title : '',
32745     /**
32746      * @cfg {String} html
32747      */   
32748     html : '',
32749     /**
32750      * @cfg {String} bgimage
32751      */   
32752     bgimage : '',
32753     /**
32754      * @cfg {String} cls
32755      */   
32756     cls : '',
32757     /**
32758      * @cfg {String} href
32759      */   
32760     href : '',
32761     /**
32762      * @cfg {String} video
32763      */   
32764     video : '',
32765     /**
32766      * @cfg {Boolean} square
32767      */   
32768     square : true,
32769     
32770     getAutoCreate : function()
32771     {
32772         var cls = 'roo-brick';
32773         
32774         if(this.href.length){
32775             cls += ' roo-brick-link';
32776         }
32777         
32778         if(this.bgimage.length){
32779             cls += ' roo-brick-image';
32780         }
32781         
32782         if(!this.html.length && !this.bgimage.length){
32783             cls += ' roo-brick-center-title';
32784         }
32785         
32786         if(!this.html.length && this.bgimage.length){
32787             cls += ' roo-brick-bottom-title';
32788         }
32789         
32790         if(this.cls){
32791             cls += ' ' + this.cls;
32792         }
32793         
32794         var cfg = {
32795             tag: (this.href.length) ? 'a' : 'div',
32796             cls: cls,
32797             cn: [
32798                 {
32799                     tag: 'div',
32800                     cls: 'roo-brick-paragraph',
32801                     cn: []
32802                 }
32803             ]
32804         };
32805         
32806         if(this.href.length){
32807             cfg.href = this.href;
32808         }
32809         
32810         var cn = cfg.cn[0].cn;
32811         
32812         if(this.title.length){
32813             cn.push({
32814                 tag: 'h4',
32815                 cls: 'roo-brick-title',
32816                 html: this.title
32817             });
32818         }
32819         
32820         if(this.html.length){
32821             cn.push({
32822                 tag: 'p',
32823                 cls: 'roo-brick-text',
32824                 html: this.html
32825             });
32826         } else {
32827             cn.cls += ' hide';
32828         }
32829         
32830         if(this.bgimage.length){
32831             cfg.cn.push({
32832                 tag: 'img',
32833                 cls: 'roo-brick-image-view',
32834                 src: this.bgimage
32835             });
32836         }
32837         
32838         return cfg;
32839     },
32840     
32841     initEvents: function() 
32842     {
32843         if(this.title.length || this.html.length){
32844             this.el.on('mouseenter'  ,this.enter, this);
32845             this.el.on('mouseleave', this.leave, this);
32846         }
32847         
32848         Roo.EventManager.onWindowResize(this.resize, this); 
32849         
32850         if(this.bgimage.length){
32851             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32852             this.imageEl.on('load', this.onImageLoad, this);
32853             return;
32854         }
32855         
32856         this.resize();
32857     },
32858     
32859     onImageLoad : function()
32860     {
32861         this.resize();
32862     },
32863     
32864     resize : function()
32865     {
32866         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32867         
32868         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32869         
32870         if(this.bgimage.length){
32871             var image = this.el.select('.roo-brick-image-view', true).first();
32872             
32873             image.setWidth(paragraph.getWidth());
32874             
32875             if(this.square){
32876                 image.setHeight(paragraph.getWidth());
32877             }
32878             
32879             this.el.setHeight(image.getHeight());
32880             paragraph.setHeight(image.getHeight());
32881             
32882         }
32883         
32884     },
32885     
32886     enter: function(e, el)
32887     {
32888         e.preventDefault();
32889         
32890         if(this.bgimage.length){
32891             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32892             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32893         }
32894     },
32895     
32896     leave: function(e, el)
32897     {
32898         e.preventDefault();
32899         
32900         if(this.bgimage.length){
32901             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32902             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32903         }
32904     }
32905     
32906 });
32907
32908  
32909
32910  /*
32911  * - LGPL
32912  *
32913  * Input
32914  * 
32915  */
32916
32917 /**
32918  * @class Roo.bootstrap.NumberField
32919  * @extends Roo.bootstrap.Input
32920  * Bootstrap NumberField class
32921  * 
32922  * 
32923  * 
32924  * 
32925  * @constructor
32926  * Create a new NumberField
32927  * @param {Object} config The config object
32928  */
32929
32930 Roo.bootstrap.NumberField = function(config){
32931     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32932 };
32933
32934 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32935     
32936     /**
32937      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32938      */
32939     allowDecimals : true,
32940     /**
32941      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32942      */
32943     decimalSeparator : ".",
32944     /**
32945      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32946      */
32947     decimalPrecision : 2,
32948     /**
32949      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32950      */
32951     allowNegative : true,
32952     /**
32953      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32954      */
32955     minValue : Number.NEGATIVE_INFINITY,
32956     /**
32957      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32958      */
32959     maxValue : Number.MAX_VALUE,
32960     /**
32961      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32962      */
32963     minText : "The minimum value for this field is {0}",
32964     /**
32965      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32966      */
32967     maxText : "The maximum value for this field is {0}",
32968     /**
32969      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32970      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32971      */
32972     nanText : "{0} is not a valid number",
32973     /**
32974      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32975      */
32976     castInt : true,
32977
32978     // private
32979     initEvents : function()
32980     {   
32981         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32982         
32983         var allowed = "0123456789";
32984         
32985         if(this.allowDecimals){
32986             allowed += this.decimalSeparator;
32987         }
32988         
32989         if(this.allowNegative){
32990             allowed += "-";
32991         }
32992         
32993         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32994         
32995         var keyPress = function(e){
32996             
32997             var k = e.getKey();
32998             
32999             var c = e.getCharCode();
33000             
33001             if(
33002                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33003                     allowed.indexOf(String.fromCharCode(c)) === -1
33004             ){
33005                 e.stopEvent();
33006                 return;
33007             }
33008             
33009             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33010                 return;
33011             }
33012             
33013             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33014                 e.stopEvent();
33015             }
33016         };
33017         
33018         this.el.on("keypress", keyPress, this);
33019     },
33020     
33021     validateValue : function(value)
33022     {
33023         
33024         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33025             return false;
33026         }
33027         
33028         var num = this.parseValue(value);
33029         
33030         if(isNaN(num)){
33031             this.markInvalid(String.format(this.nanText, value));
33032             return false;
33033         }
33034         
33035         if(num < this.minValue){
33036             this.markInvalid(String.format(this.minText, this.minValue));
33037             return false;
33038         }
33039         
33040         if(num > this.maxValue){
33041             this.markInvalid(String.format(this.maxText, this.maxValue));
33042             return false;
33043         }
33044         
33045         return true;
33046     },
33047
33048     getValue : function()
33049     {
33050         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33051     },
33052
33053     parseValue : function(value)
33054     {
33055         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33056         return isNaN(value) ? '' : value;
33057     },
33058
33059     fixPrecision : function(value)
33060     {
33061         var nan = isNaN(value);
33062         
33063         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33064             return nan ? '' : value;
33065         }
33066         return parseFloat(value).toFixed(this.decimalPrecision);
33067     },
33068
33069     setValue : function(v)
33070     {
33071         v = this.fixPrecision(v);
33072         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33073     },
33074
33075     decimalPrecisionFcn : function(v)
33076     {
33077         return Math.floor(v);
33078     },
33079
33080     beforeBlur : function()
33081     {
33082         if(!this.castInt){
33083             return;
33084         }
33085         
33086         var v = this.parseValue(this.getRawValue());
33087         if(v){
33088             this.setValue(v);
33089         }
33090     }
33091     
33092 });
33093
33094  
33095
33096 /*
33097 * Licence: LGPL
33098 */
33099
33100 /**
33101  * @class Roo.bootstrap.DocumentSlider
33102  * @extends Roo.bootstrap.Component
33103  * Bootstrap DocumentSlider class
33104  * 
33105  * @constructor
33106  * Create a new DocumentViewer
33107  * @param {Object} config The config object
33108  */
33109
33110 Roo.bootstrap.DocumentSlider = function(config){
33111     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33112     
33113     this.files = [];
33114     
33115     this.addEvents({
33116         /**
33117          * @event initial
33118          * Fire after initEvent
33119          * @param {Roo.bootstrap.DocumentSlider} this
33120          */
33121         "initial" : true,
33122         /**
33123          * @event update
33124          * Fire after update
33125          * @param {Roo.bootstrap.DocumentSlider} this
33126          */
33127         "update" : true,
33128         /**
33129          * @event click
33130          * Fire after click
33131          * @param {Roo.bootstrap.DocumentSlider} this
33132          */
33133         "click" : true
33134     });
33135 };
33136
33137 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33138     
33139     files : false,
33140     
33141     indicator : 0,
33142     
33143     getAutoCreate : function()
33144     {
33145         var cfg = {
33146             tag : 'div',
33147             cls : 'roo-document-slider',
33148             cn : [
33149                 {
33150                     tag : 'div',
33151                     cls : 'roo-document-slider-header',
33152                     cn : [
33153                         {
33154                             tag : 'div',
33155                             cls : 'roo-document-slider-header-title'
33156                         }
33157                     ]
33158                 },
33159                 {
33160                     tag : 'div',
33161                     cls : 'roo-document-slider-body',
33162                     cn : [
33163                         {
33164                             tag : 'div',
33165                             cls : 'roo-document-slider-prev',
33166                             cn : [
33167                                 {
33168                                     tag : 'i',
33169                                     cls : 'fa fa-chevron-left'
33170                                 }
33171                             ]
33172                         },
33173                         {
33174                             tag : 'div',
33175                             cls : 'roo-document-slider-thumb',
33176                             cn : [
33177                                 {
33178                                     tag : 'img',
33179                                     cls : 'roo-document-slider-image'
33180                                 }
33181                             ]
33182                         },
33183                         {
33184                             tag : 'div',
33185                             cls : 'roo-document-slider-next',
33186                             cn : [
33187                                 {
33188                                     tag : 'i',
33189                                     cls : 'fa fa-chevron-right'
33190                                 }
33191                             ]
33192                         }
33193                     ]
33194                 }
33195             ]
33196         };
33197         
33198         return cfg;
33199     },
33200     
33201     initEvents : function()
33202     {
33203         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33204         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33205         
33206         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33207         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33208         
33209         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33210         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33211         
33212         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33213         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33214         
33215         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33216         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33217         
33218         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33219         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33220         
33221         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33222         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33223         
33224         this.thumbEl.on('click', this.onClick, this);
33225         
33226         this.prevIndicator.on('click', this.prev, this);
33227         
33228         this.nextIndicator.on('click', this.next, this);
33229         
33230     },
33231     
33232     initial : function()
33233     {
33234         if(this.files.length){
33235             this.indicator = 1;
33236             this.update()
33237         }
33238         
33239         this.fireEvent('initial', this);
33240     },
33241     
33242     update : function()
33243     {
33244         this.imageEl.attr('src', this.files[this.indicator - 1]);
33245         
33246         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33247         
33248         this.prevIndicator.show();
33249         
33250         if(this.indicator == 1){
33251             this.prevIndicator.hide();
33252         }
33253         
33254         this.nextIndicator.show();
33255         
33256         if(this.indicator == this.files.length){
33257             this.nextIndicator.hide();
33258         }
33259         
33260         this.thumbEl.scrollTo('top');
33261         
33262         this.fireEvent('update', this);
33263     },
33264     
33265     onClick : function(e)
33266     {
33267         e.preventDefault();
33268         
33269         this.fireEvent('click', this);
33270     },
33271     
33272     prev : function(e)
33273     {
33274         e.preventDefault();
33275         
33276         this.indicator = Math.max(1, this.indicator - 1);
33277         
33278         this.update();
33279     },
33280     
33281     next : function(e)
33282     {
33283         e.preventDefault();
33284         
33285         this.indicator = Math.min(this.files.length, this.indicator + 1);
33286         
33287         this.update();
33288     }
33289 });
33290 /*
33291  * - LGPL
33292  *
33293  * RadioSet
33294  *
33295  *
33296  */
33297
33298 /**
33299  * @class Roo.bootstrap.RadioSet
33300  * @extends Roo.bootstrap.Input
33301  * Bootstrap RadioSet class
33302  * @cfg {String} indicatorpos (left|right) default left
33303  * @cfg {Boolean} inline (true|false) inline the element (default true)
33304  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33305  * @constructor
33306  * Create a new RadioSet
33307  * @param {Object} config The config object
33308  */
33309
33310 Roo.bootstrap.RadioSet = function(config){
33311     
33312     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33313     
33314     this.radioes = [];
33315     
33316     Roo.bootstrap.RadioSet.register(this);
33317     
33318     this.addEvents({
33319         /**
33320         * @event check
33321         * Fires when the element is checked or unchecked.
33322         * @param {Roo.bootstrap.RadioSet} this This radio
33323         * @param {Roo.bootstrap.Radio} item The checked item
33324         */
33325        check : true
33326     });
33327     
33328 };
33329
33330 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33331
33332     radioes : false,
33333     
33334     inline : true,
33335     
33336     weight : '',
33337     
33338     indicatorpos : 'left',
33339     
33340     getAutoCreate : function()
33341     {
33342         var label = {
33343             tag : 'label',
33344             cls : 'roo-radio-set-label',
33345             cn : [
33346                 {
33347                     tag : 'span',
33348                     html : this.fieldLabel
33349                 }
33350             ]
33351         };
33352         
33353         if(this.indicatorpos == 'left'){
33354             label.cn.unshift({
33355                 tag : 'i',
33356                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33357                 tooltip : 'This field is required'
33358             });
33359         } else {
33360             label.cn.push({
33361                 tag : 'i',
33362                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33363                 tooltip : 'This field is required'
33364             });
33365         }
33366         
33367         var items = {
33368             tag : 'div',
33369             cls : 'roo-radio-set-items'
33370         };
33371         
33372         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33373         
33374         if (align === 'left' && this.fieldLabel.length) {
33375             
33376             items = {
33377                 cls : "roo-radio-set-right", 
33378                 cn: [
33379                     items
33380                 ]
33381             };
33382             
33383             if(this.labelWidth > 12){
33384                 label.style = "width: " + this.labelWidth + 'px';
33385             }
33386             
33387             if(this.labelWidth < 13 && this.labelmd == 0){
33388                 this.labelmd = this.labelWidth;
33389             }
33390             
33391             if(this.labellg > 0){
33392                 label.cls += ' col-lg-' + this.labellg;
33393                 items.cls += ' col-lg-' + (12 - this.labellg);
33394             }
33395             
33396             if(this.labelmd > 0){
33397                 label.cls += ' col-md-' + this.labelmd;
33398                 items.cls += ' col-md-' + (12 - this.labelmd);
33399             }
33400             
33401             if(this.labelsm > 0){
33402                 label.cls += ' col-sm-' + this.labelsm;
33403                 items.cls += ' col-sm-' + (12 - this.labelsm);
33404             }
33405             
33406             if(this.labelxs > 0){
33407                 label.cls += ' col-xs-' + this.labelxs;
33408                 items.cls += ' col-xs-' + (12 - this.labelxs);
33409             }
33410         }
33411         
33412         var cfg = {
33413             tag : 'div',
33414             cls : 'roo-radio-set',
33415             cn : [
33416                 {
33417                     tag : 'input',
33418                     cls : 'roo-radio-set-input',
33419                     type : 'hidden',
33420                     name : this.name,
33421                     value : this.value ? this.value :  ''
33422                 },
33423                 label,
33424                 items
33425             ]
33426         };
33427         
33428         if(this.weight.length){
33429             cfg.cls += ' roo-radio-' + this.weight;
33430         }
33431         
33432         if(this.inline) {
33433             cfg.cls += ' roo-radio-set-inline';
33434         }
33435         
33436         var settings=this;
33437         ['xs','sm','md','lg'].map(function(size){
33438             if (settings[size]) {
33439                 cfg.cls += ' col-' + size + '-' + settings[size];
33440             }
33441         });
33442         
33443         return cfg;
33444         
33445     },
33446
33447     initEvents : function()
33448     {
33449         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33450         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33451         
33452         if(!this.fieldLabel.length){
33453             this.labelEl.hide();
33454         }
33455         
33456         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33457         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33458         
33459         this.indicatorEl().addClass('invisible');
33460         
33461         this.originalValue = this.getValue();
33462         
33463     },
33464     
33465     inputEl: function ()
33466     {
33467         return this.el.select('.roo-radio-set-input', true).first();
33468     },
33469     
33470     getChildContainer : function()
33471     {
33472         return this.itemsEl;
33473     },
33474     
33475     register : function(item)
33476     {
33477         this.radioes.push(item);
33478         
33479     },
33480     
33481     validate : function()
33482     {   
33483         var valid = false;
33484         
33485         Roo.each(this.radioes, function(i){
33486             if(!i.checked){
33487                 return;
33488             }
33489             
33490             valid = true;
33491             return false;
33492         });
33493         
33494         if(this.allowBlank) {
33495             return true;
33496         }
33497         
33498         if(this.disabled || valid){
33499             this.markValid();
33500             return true;
33501         }
33502         
33503         this.markInvalid();
33504         return false;
33505         
33506     },
33507     
33508     markValid : function()
33509     {
33510         if(this.labelEl.isVisible(true)){
33511             this.indicatorEl().removeClass('visible');
33512             this.indicatorEl().addClass('invisible');
33513         }
33514         
33515         this.el.removeClass([this.invalidClass, this.validClass]);
33516         this.el.addClass(this.validClass);
33517         
33518         this.fireEvent('valid', this);
33519     },
33520     
33521     markInvalid : function(msg)
33522     {
33523         if(this.allowBlank || this.disabled){
33524             return;
33525         }
33526         
33527         if(this.labelEl.isVisible(true)){
33528             this.indicatorEl().removeClass('invisible');
33529             this.indicatorEl().addClass('visible');
33530         }
33531         
33532         this.el.removeClass([this.invalidClass, this.validClass]);
33533         this.el.addClass(this.invalidClass);
33534         
33535         this.fireEvent('invalid', this, msg);
33536         
33537     },
33538     
33539     setValue : function(v, suppressEvent)
33540     {   
33541         if(this.value === v){
33542             return;
33543         }
33544         
33545         this.value = v;
33546         
33547         if(this.rendered){
33548             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33549         }
33550         
33551         Roo.each(this.radioes, function(i){
33552             
33553             i.checked = false;
33554             i.el.removeClass('checked');
33555             
33556             if(i.value === v || i.value.toString() === v.toString()){
33557                 i.checked = true;
33558                 i.el.addClass('checked');
33559                 
33560                 if(suppressEvent !== true){
33561                     this.fireEvent('check', this, i);
33562                 }
33563             }
33564             
33565         }, this);
33566         
33567         this.validate();
33568     },
33569     
33570     clearInvalid : function(){
33571         
33572         if(!this.el || this.preventMark){
33573             return;
33574         }
33575         
33576         this.el.removeClass([this.invalidClass]);
33577         
33578         this.fireEvent('valid', this);
33579     }
33580     
33581 });
33582
33583 Roo.apply(Roo.bootstrap.RadioSet, {
33584     
33585     groups: {},
33586     
33587     register : function(set)
33588     {
33589         this.groups[set.name] = set;
33590     },
33591     
33592     get: function(name) 
33593     {
33594         if (typeof(this.groups[name]) == 'undefined') {
33595             return false;
33596         }
33597         
33598         return this.groups[name] ;
33599     }
33600     
33601 });
33602 /*
33603  * Based on:
33604  * Ext JS Library 1.1.1
33605  * Copyright(c) 2006-2007, Ext JS, LLC.
33606  *
33607  * Originally Released Under LGPL - original licence link has changed is not relivant.
33608  *
33609  * Fork - LGPL
33610  * <script type="text/javascript">
33611  */
33612
33613
33614 /**
33615  * @class Roo.bootstrap.SplitBar
33616  * @extends Roo.util.Observable
33617  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33618  * <br><br>
33619  * Usage:
33620  * <pre><code>
33621 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33622                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33623 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33624 split.minSize = 100;
33625 split.maxSize = 600;
33626 split.animate = true;
33627 split.on('moved', splitterMoved);
33628 </code></pre>
33629  * @constructor
33630  * Create a new SplitBar
33631  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33632  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33633  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33634  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33635                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33636                         position of the SplitBar).
33637  */
33638 Roo.bootstrap.SplitBar = function(cfg){
33639     
33640     /** @private */
33641     
33642     //{
33643     //  dragElement : elm
33644     //  resizingElement: el,
33645         // optional..
33646     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33647     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33648         // existingProxy ???
33649     //}
33650     
33651     this.el = Roo.get(cfg.dragElement, true);
33652     this.el.dom.unselectable = "on";
33653     /** @private */
33654     this.resizingEl = Roo.get(cfg.resizingElement, true);
33655
33656     /**
33657      * @private
33658      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33659      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33660      * @type Number
33661      */
33662     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33663     
33664     /**
33665      * The minimum size of the resizing element. (Defaults to 0)
33666      * @type Number
33667      */
33668     this.minSize = 0;
33669     
33670     /**
33671      * The maximum size of the resizing element. (Defaults to 2000)
33672      * @type Number
33673      */
33674     this.maxSize = 2000;
33675     
33676     /**
33677      * Whether to animate the transition to the new size
33678      * @type Boolean
33679      */
33680     this.animate = false;
33681     
33682     /**
33683      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33684      * @type Boolean
33685      */
33686     this.useShim = false;
33687     
33688     /** @private */
33689     this.shim = null;
33690     
33691     if(!cfg.existingProxy){
33692         /** @private */
33693         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33694     }else{
33695         this.proxy = Roo.get(cfg.existingProxy).dom;
33696     }
33697     /** @private */
33698     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33699     
33700     /** @private */
33701     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33702     
33703     /** @private */
33704     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33705     
33706     /** @private */
33707     this.dragSpecs = {};
33708     
33709     /**
33710      * @private The adapter to use to positon and resize elements
33711      */
33712     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33713     this.adapter.init(this);
33714     
33715     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33716         /** @private */
33717         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33718         this.el.addClass("roo-splitbar-h");
33719     }else{
33720         /** @private */
33721         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33722         this.el.addClass("roo-splitbar-v");
33723     }
33724     
33725     this.addEvents({
33726         /**
33727          * @event resize
33728          * Fires when the splitter is moved (alias for {@link #event-moved})
33729          * @param {Roo.bootstrap.SplitBar} this
33730          * @param {Number} newSize the new width or height
33731          */
33732         "resize" : true,
33733         /**
33734          * @event moved
33735          * Fires when the splitter is moved
33736          * @param {Roo.bootstrap.SplitBar} this
33737          * @param {Number} newSize the new width or height
33738          */
33739         "moved" : true,
33740         /**
33741          * @event beforeresize
33742          * Fires before the splitter is dragged
33743          * @param {Roo.bootstrap.SplitBar} this
33744          */
33745         "beforeresize" : true,
33746
33747         "beforeapply" : true
33748     });
33749
33750     Roo.util.Observable.call(this);
33751 };
33752
33753 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33754     onStartProxyDrag : function(x, y){
33755         this.fireEvent("beforeresize", this);
33756         if(!this.overlay){
33757             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33758             o.unselectable();
33759             o.enableDisplayMode("block");
33760             // all splitbars share the same overlay
33761             Roo.bootstrap.SplitBar.prototype.overlay = o;
33762         }
33763         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33764         this.overlay.show();
33765         Roo.get(this.proxy).setDisplayed("block");
33766         var size = this.adapter.getElementSize(this);
33767         this.activeMinSize = this.getMinimumSize();;
33768         this.activeMaxSize = this.getMaximumSize();;
33769         var c1 = size - this.activeMinSize;
33770         var c2 = Math.max(this.activeMaxSize - size, 0);
33771         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33772             this.dd.resetConstraints();
33773             this.dd.setXConstraint(
33774                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33775                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33776             );
33777             this.dd.setYConstraint(0, 0);
33778         }else{
33779             this.dd.resetConstraints();
33780             this.dd.setXConstraint(0, 0);
33781             this.dd.setYConstraint(
33782                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33783                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33784             );
33785          }
33786         this.dragSpecs.startSize = size;
33787         this.dragSpecs.startPoint = [x, y];
33788         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33789     },
33790     
33791     /** 
33792      * @private Called after the drag operation by the DDProxy
33793      */
33794     onEndProxyDrag : function(e){
33795         Roo.get(this.proxy).setDisplayed(false);
33796         var endPoint = Roo.lib.Event.getXY(e);
33797         if(this.overlay){
33798             this.overlay.hide();
33799         }
33800         var newSize;
33801         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33802             newSize = this.dragSpecs.startSize + 
33803                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33804                     endPoint[0] - this.dragSpecs.startPoint[0] :
33805                     this.dragSpecs.startPoint[0] - endPoint[0]
33806                 );
33807         }else{
33808             newSize = this.dragSpecs.startSize + 
33809                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33810                     endPoint[1] - this.dragSpecs.startPoint[1] :
33811                     this.dragSpecs.startPoint[1] - endPoint[1]
33812                 );
33813         }
33814         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33815         if(newSize != this.dragSpecs.startSize){
33816             if(this.fireEvent('beforeapply', this, newSize) !== false){
33817                 this.adapter.setElementSize(this, newSize);
33818                 this.fireEvent("moved", this, newSize);
33819                 this.fireEvent("resize", this, newSize);
33820             }
33821         }
33822     },
33823     
33824     /**
33825      * Get the adapter this SplitBar uses
33826      * @return The adapter object
33827      */
33828     getAdapter : function(){
33829         return this.adapter;
33830     },
33831     
33832     /**
33833      * Set the adapter this SplitBar uses
33834      * @param {Object} adapter A SplitBar adapter object
33835      */
33836     setAdapter : function(adapter){
33837         this.adapter = adapter;
33838         this.adapter.init(this);
33839     },
33840     
33841     /**
33842      * Gets the minimum size for the resizing element
33843      * @return {Number} The minimum size
33844      */
33845     getMinimumSize : function(){
33846         return this.minSize;
33847     },
33848     
33849     /**
33850      * Sets the minimum size for the resizing element
33851      * @param {Number} minSize The minimum size
33852      */
33853     setMinimumSize : function(minSize){
33854         this.minSize = minSize;
33855     },
33856     
33857     /**
33858      * Gets the maximum size for the resizing element
33859      * @return {Number} The maximum size
33860      */
33861     getMaximumSize : function(){
33862         return this.maxSize;
33863     },
33864     
33865     /**
33866      * Sets the maximum size for the resizing element
33867      * @param {Number} maxSize The maximum size
33868      */
33869     setMaximumSize : function(maxSize){
33870         this.maxSize = maxSize;
33871     },
33872     
33873     /**
33874      * Sets the initialize size for the resizing element
33875      * @param {Number} size The initial size
33876      */
33877     setCurrentSize : function(size){
33878         var oldAnimate = this.animate;
33879         this.animate = false;
33880         this.adapter.setElementSize(this, size);
33881         this.animate = oldAnimate;
33882     },
33883     
33884     /**
33885      * Destroy this splitbar. 
33886      * @param {Boolean} removeEl True to remove the element
33887      */
33888     destroy : function(removeEl){
33889         if(this.shim){
33890             this.shim.remove();
33891         }
33892         this.dd.unreg();
33893         this.proxy.parentNode.removeChild(this.proxy);
33894         if(removeEl){
33895             this.el.remove();
33896         }
33897     }
33898 });
33899
33900 /**
33901  * @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.
33902  */
33903 Roo.bootstrap.SplitBar.createProxy = function(dir){
33904     var proxy = new Roo.Element(document.createElement("div"));
33905     proxy.unselectable();
33906     var cls = 'roo-splitbar-proxy';
33907     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33908     document.body.appendChild(proxy.dom);
33909     return proxy.dom;
33910 };
33911
33912 /** 
33913  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33914  * Default Adapter. It assumes the splitter and resizing element are not positioned
33915  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33916  */
33917 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33918 };
33919
33920 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33921     // do nothing for now
33922     init : function(s){
33923     
33924     },
33925     /**
33926      * Called before drag operations to get the current size of the resizing element. 
33927      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33928      */
33929      getElementSize : function(s){
33930         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33931             return s.resizingEl.getWidth();
33932         }else{
33933             return s.resizingEl.getHeight();
33934         }
33935     },
33936     
33937     /**
33938      * Called after drag operations to set the size of the resizing element.
33939      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33940      * @param {Number} newSize The new size to set
33941      * @param {Function} onComplete A function to be invoked when resizing is complete
33942      */
33943     setElementSize : function(s, newSize, onComplete){
33944         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33945             if(!s.animate){
33946                 s.resizingEl.setWidth(newSize);
33947                 if(onComplete){
33948                     onComplete(s, newSize);
33949                 }
33950             }else{
33951                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33952             }
33953         }else{
33954             
33955             if(!s.animate){
33956                 s.resizingEl.setHeight(newSize);
33957                 if(onComplete){
33958                     onComplete(s, newSize);
33959                 }
33960             }else{
33961                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33962             }
33963         }
33964     }
33965 };
33966
33967 /** 
33968  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33969  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33970  * Adapter that  moves the splitter element to align with the resized sizing element. 
33971  * Used with an absolute positioned SplitBar.
33972  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33973  * document.body, make sure you assign an id to the body element.
33974  */
33975 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33976     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33977     this.container = Roo.get(container);
33978 };
33979
33980 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33981     init : function(s){
33982         this.basic.init(s);
33983     },
33984     
33985     getElementSize : function(s){
33986         return this.basic.getElementSize(s);
33987     },
33988     
33989     setElementSize : function(s, newSize, onComplete){
33990         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33991     },
33992     
33993     moveSplitter : function(s){
33994         var yes = Roo.bootstrap.SplitBar;
33995         switch(s.placement){
33996             case yes.LEFT:
33997                 s.el.setX(s.resizingEl.getRight());
33998                 break;
33999             case yes.RIGHT:
34000                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34001                 break;
34002             case yes.TOP:
34003                 s.el.setY(s.resizingEl.getBottom());
34004                 break;
34005             case yes.BOTTOM:
34006                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34007                 break;
34008         }
34009     }
34010 };
34011
34012 /**
34013  * Orientation constant - Create a vertical SplitBar
34014  * @static
34015  * @type Number
34016  */
34017 Roo.bootstrap.SplitBar.VERTICAL = 1;
34018
34019 /**
34020  * Orientation constant - Create a horizontal SplitBar
34021  * @static
34022  * @type Number
34023  */
34024 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34025
34026 /**
34027  * Placement constant - The resizing element is to the left of the splitter element
34028  * @static
34029  * @type Number
34030  */
34031 Roo.bootstrap.SplitBar.LEFT = 1;
34032
34033 /**
34034  * Placement constant - The resizing element is to the right of the splitter element
34035  * @static
34036  * @type Number
34037  */
34038 Roo.bootstrap.SplitBar.RIGHT = 2;
34039
34040 /**
34041  * Placement constant - The resizing element is positioned above the splitter element
34042  * @static
34043  * @type Number
34044  */
34045 Roo.bootstrap.SplitBar.TOP = 3;
34046
34047 /**
34048  * Placement constant - The resizing element is positioned under splitter element
34049  * @static
34050  * @type Number
34051  */
34052 Roo.bootstrap.SplitBar.BOTTOM = 4;
34053 Roo.namespace("Roo.bootstrap.layout");/*
34054  * Based on:
34055  * Ext JS Library 1.1.1
34056  * Copyright(c) 2006-2007, Ext JS, LLC.
34057  *
34058  * Originally Released Under LGPL - original licence link has changed is not relivant.
34059  *
34060  * Fork - LGPL
34061  * <script type="text/javascript">
34062  */
34063
34064 /**
34065  * @class Roo.bootstrap.layout.Manager
34066  * @extends Roo.bootstrap.Component
34067  * Base class for layout managers.
34068  */
34069 Roo.bootstrap.layout.Manager = function(config)
34070 {
34071     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34072
34073
34074
34075
34076
34077     /** false to disable window resize monitoring @type Boolean */
34078     this.monitorWindowResize = true;
34079     this.regions = {};
34080     this.addEvents({
34081         /**
34082          * @event layout
34083          * Fires when a layout is performed.
34084          * @param {Roo.LayoutManager} this
34085          */
34086         "layout" : true,
34087         /**
34088          * @event regionresized
34089          * Fires when the user resizes a region.
34090          * @param {Roo.LayoutRegion} region The resized region
34091          * @param {Number} newSize The new size (width for east/west, height for north/south)
34092          */
34093         "regionresized" : true,
34094         /**
34095          * @event regioncollapsed
34096          * Fires when a region is collapsed.
34097          * @param {Roo.LayoutRegion} region The collapsed region
34098          */
34099         "regioncollapsed" : true,
34100         /**
34101          * @event regionexpanded
34102          * Fires when a region is expanded.
34103          * @param {Roo.LayoutRegion} region The expanded region
34104          */
34105         "regionexpanded" : true
34106     });
34107     this.updating = false;
34108
34109     if (config.el) {
34110         this.el = Roo.get(config.el);
34111         this.initEvents();
34112     }
34113
34114 };
34115
34116 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34117
34118
34119     regions : null,
34120
34121     monitorWindowResize : true,
34122
34123
34124     updating : false,
34125
34126
34127     onRender : function(ct, position)
34128     {
34129         if(!this.el){
34130             this.el = Roo.get(ct);
34131             this.initEvents();
34132         }
34133         //this.fireEvent('render',this);
34134     },
34135
34136
34137     initEvents: function()
34138     {
34139
34140
34141         // ie scrollbar fix
34142         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34143             document.body.scroll = "no";
34144         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34145             this.el.position('relative');
34146         }
34147         this.id = this.el.id;
34148         this.el.addClass("roo-layout-container");
34149         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34150         if(this.el.dom != document.body ) {
34151             this.el.on('resize', this.layout,this);
34152             this.el.on('show', this.layout,this);
34153         }
34154
34155     },
34156
34157     /**
34158      * Returns true if this layout is currently being updated
34159      * @return {Boolean}
34160      */
34161     isUpdating : function(){
34162         return this.updating;
34163     },
34164
34165     /**
34166      * Suspend the LayoutManager from doing auto-layouts while
34167      * making multiple add or remove calls
34168      */
34169     beginUpdate : function(){
34170         this.updating = true;
34171     },
34172
34173     /**
34174      * Restore auto-layouts and optionally disable the manager from performing a layout
34175      * @param {Boolean} noLayout true to disable a layout update
34176      */
34177     endUpdate : function(noLayout){
34178         this.updating = false;
34179         if(!noLayout){
34180             this.layout();
34181         }
34182     },
34183
34184     layout: function(){
34185         // abstract...
34186     },
34187
34188     onRegionResized : function(region, newSize){
34189         this.fireEvent("regionresized", region, newSize);
34190         this.layout();
34191     },
34192
34193     onRegionCollapsed : function(region){
34194         this.fireEvent("regioncollapsed", region);
34195     },
34196
34197     onRegionExpanded : function(region){
34198         this.fireEvent("regionexpanded", region);
34199     },
34200
34201     /**
34202      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34203      * performs box-model adjustments.
34204      * @return {Object} The size as an object {width: (the width), height: (the height)}
34205      */
34206     getViewSize : function()
34207     {
34208         var size;
34209         if(this.el.dom != document.body){
34210             size = this.el.getSize();
34211         }else{
34212             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34213         }
34214         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34215         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34216         return size;
34217     },
34218
34219     /**
34220      * Returns the Element this layout is bound to.
34221      * @return {Roo.Element}
34222      */
34223     getEl : function(){
34224         return this.el;
34225     },
34226
34227     /**
34228      * Returns the specified region.
34229      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34230      * @return {Roo.LayoutRegion}
34231      */
34232     getRegion : function(target){
34233         return this.regions[target.toLowerCase()];
34234     },
34235
34236     onWindowResize : function(){
34237         if(this.monitorWindowResize){
34238             this.layout();
34239         }
34240     }
34241 });
34242 /*
34243  * Based on:
34244  * Ext JS Library 1.1.1
34245  * Copyright(c) 2006-2007, Ext JS, LLC.
34246  *
34247  * Originally Released Under LGPL - original licence link has changed is not relivant.
34248  *
34249  * Fork - LGPL
34250  * <script type="text/javascript">
34251  */
34252 /**
34253  * @class Roo.bootstrap.layout.Border
34254  * @extends Roo.bootstrap.layout.Manager
34255  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34256  * please see: examples/bootstrap/nested.html<br><br>
34257  
34258 <b>The container the layout is rendered into can be either the body element or any other element.
34259 If it is not the body element, the container needs to either be an absolute positioned element,
34260 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34261 the container size if it is not the body element.</b>
34262
34263 * @constructor
34264 * Create a new Border
34265 * @param {Object} config Configuration options
34266  */
34267 Roo.bootstrap.layout.Border = function(config){
34268     config = config || {};
34269     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34270     
34271     
34272     
34273     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34274         if(config[region]){
34275             config[region].region = region;
34276             this.addRegion(config[region]);
34277         }
34278     },this);
34279     
34280 };
34281
34282 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34283
34284 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34285     /**
34286      * Creates and adds a new region if it doesn't already exist.
34287      * @param {String} target The target region key (north, south, east, west or center).
34288      * @param {Object} config The regions config object
34289      * @return {BorderLayoutRegion} The new region
34290      */
34291     addRegion : function(config)
34292     {
34293         if(!this.regions[config.region]){
34294             var r = this.factory(config);
34295             this.bindRegion(r);
34296         }
34297         return this.regions[config.region];
34298     },
34299
34300     // private (kinda)
34301     bindRegion : function(r){
34302         this.regions[r.config.region] = r;
34303         
34304         r.on("visibilitychange",    this.layout, this);
34305         r.on("paneladded",          this.layout, this);
34306         r.on("panelremoved",        this.layout, this);
34307         r.on("invalidated",         this.layout, this);
34308         r.on("resized",             this.onRegionResized, this);
34309         r.on("collapsed",           this.onRegionCollapsed, this);
34310         r.on("expanded",            this.onRegionExpanded, this);
34311     },
34312
34313     /**
34314      * Performs a layout update.
34315      */
34316     layout : function()
34317     {
34318         if(this.updating) {
34319             return;
34320         }
34321         
34322         // render all the rebions if they have not been done alreayd?
34323         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34324             if(this.regions[region] && !this.regions[region].bodyEl){
34325                 this.regions[region].onRender(this.el)
34326             }
34327         },this);
34328         
34329         var size = this.getViewSize();
34330         var w = size.width;
34331         var h = size.height;
34332         var centerW = w;
34333         var centerH = h;
34334         var centerY = 0;
34335         var centerX = 0;
34336         //var x = 0, y = 0;
34337
34338         var rs = this.regions;
34339         var north = rs["north"];
34340         var south = rs["south"]; 
34341         var west = rs["west"];
34342         var east = rs["east"];
34343         var center = rs["center"];
34344         //if(this.hideOnLayout){ // not supported anymore
34345             //c.el.setStyle("display", "none");
34346         //}
34347         if(north && north.isVisible()){
34348             var b = north.getBox();
34349             var m = north.getMargins();
34350             b.width = w - (m.left+m.right);
34351             b.x = m.left;
34352             b.y = m.top;
34353             centerY = b.height + b.y + m.bottom;
34354             centerH -= centerY;
34355             north.updateBox(this.safeBox(b));
34356         }
34357         if(south && south.isVisible()){
34358             var b = south.getBox();
34359             var m = south.getMargins();
34360             b.width = w - (m.left+m.right);
34361             b.x = m.left;
34362             var totalHeight = (b.height + m.top + m.bottom);
34363             b.y = h - totalHeight + m.top;
34364             centerH -= totalHeight;
34365             south.updateBox(this.safeBox(b));
34366         }
34367         if(west && west.isVisible()){
34368             var b = west.getBox();
34369             var m = west.getMargins();
34370             b.height = centerH - (m.top+m.bottom);
34371             b.x = m.left;
34372             b.y = centerY + m.top;
34373             var totalWidth = (b.width + m.left + m.right);
34374             centerX += totalWidth;
34375             centerW -= totalWidth;
34376             west.updateBox(this.safeBox(b));
34377         }
34378         if(east && east.isVisible()){
34379             var b = east.getBox();
34380             var m = east.getMargins();
34381             b.height = centerH - (m.top+m.bottom);
34382             var totalWidth = (b.width + m.left + m.right);
34383             b.x = w - totalWidth + m.left;
34384             b.y = centerY + m.top;
34385             centerW -= totalWidth;
34386             east.updateBox(this.safeBox(b));
34387         }
34388         if(center){
34389             var m = center.getMargins();
34390             var centerBox = {
34391                 x: centerX + m.left,
34392                 y: centerY + m.top,
34393                 width: centerW - (m.left+m.right),
34394                 height: centerH - (m.top+m.bottom)
34395             };
34396             //if(this.hideOnLayout){
34397                 //center.el.setStyle("display", "block");
34398             //}
34399             center.updateBox(this.safeBox(centerBox));
34400         }
34401         this.el.repaint();
34402         this.fireEvent("layout", this);
34403     },
34404
34405     // private
34406     safeBox : function(box){
34407         box.width = Math.max(0, box.width);
34408         box.height = Math.max(0, box.height);
34409         return box;
34410     },
34411
34412     /**
34413      * Adds a ContentPanel (or subclass) to this layout.
34414      * @param {String} target The target region key (north, south, east, west or center).
34415      * @param {Roo.ContentPanel} panel The panel to add
34416      * @return {Roo.ContentPanel} The added panel
34417      */
34418     add : function(target, panel){
34419          
34420         target = target.toLowerCase();
34421         return this.regions[target].add(panel);
34422     },
34423
34424     /**
34425      * Remove a ContentPanel (or subclass) to this layout.
34426      * @param {String} target The target region key (north, south, east, west or center).
34427      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34428      * @return {Roo.ContentPanel} The removed panel
34429      */
34430     remove : function(target, panel){
34431         target = target.toLowerCase();
34432         return this.regions[target].remove(panel);
34433     },
34434
34435     /**
34436      * Searches all regions for a panel with the specified id
34437      * @param {String} panelId
34438      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34439      */
34440     findPanel : function(panelId){
34441         var rs = this.regions;
34442         for(var target in rs){
34443             if(typeof rs[target] != "function"){
34444                 var p = rs[target].getPanel(panelId);
34445                 if(p){
34446                     return p;
34447                 }
34448             }
34449         }
34450         return null;
34451     },
34452
34453     /**
34454      * Searches all regions for a panel with the specified id and activates (shows) it.
34455      * @param {String/ContentPanel} panelId The panels id or the panel itself
34456      * @return {Roo.ContentPanel} The shown panel or null
34457      */
34458     showPanel : function(panelId) {
34459       var rs = this.regions;
34460       for(var target in rs){
34461          var r = rs[target];
34462          if(typeof r != "function"){
34463             if(r.hasPanel(panelId)){
34464                return r.showPanel(panelId);
34465             }
34466          }
34467       }
34468       return null;
34469    },
34470
34471    /**
34472      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34473      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34474      */
34475    /*
34476     restoreState : function(provider){
34477         if(!provider){
34478             provider = Roo.state.Manager;
34479         }
34480         var sm = new Roo.LayoutStateManager();
34481         sm.init(this, provider);
34482     },
34483 */
34484  
34485  
34486     /**
34487      * Adds a xtype elements to the layout.
34488      * <pre><code>
34489
34490 layout.addxtype({
34491        xtype : 'ContentPanel',
34492        region: 'west',
34493        items: [ .... ]
34494    }
34495 );
34496
34497 layout.addxtype({
34498         xtype : 'NestedLayoutPanel',
34499         region: 'west',
34500         layout: {
34501            center: { },
34502            west: { }   
34503         },
34504         items : [ ... list of content panels or nested layout panels.. ]
34505    }
34506 );
34507 </code></pre>
34508      * @param {Object} cfg Xtype definition of item to add.
34509      */
34510     addxtype : function(cfg)
34511     {
34512         // basically accepts a pannel...
34513         // can accept a layout region..!?!?
34514         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34515         
34516         
34517         // theory?  children can only be panels??
34518         
34519         //if (!cfg.xtype.match(/Panel$/)) {
34520         //    return false;
34521         //}
34522         var ret = false;
34523         
34524         if (typeof(cfg.region) == 'undefined') {
34525             Roo.log("Failed to add Panel, region was not set");
34526             Roo.log(cfg);
34527             return false;
34528         }
34529         var region = cfg.region;
34530         delete cfg.region;
34531         
34532           
34533         var xitems = [];
34534         if (cfg.items) {
34535             xitems = cfg.items;
34536             delete cfg.items;
34537         }
34538         var nb = false;
34539         
34540         switch(cfg.xtype) 
34541         {
34542             case 'Content':  // ContentPanel (el, cfg)
34543             case 'Scroll':  // ContentPanel (el, cfg)
34544             case 'View': 
34545                 cfg.autoCreate = true;
34546                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34547                 //} else {
34548                 //    var el = this.el.createChild();
34549                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34550                 //}
34551                 
34552                 this.add(region, ret);
34553                 break;
34554             
34555             /*
34556             case 'TreePanel': // our new panel!
34557                 cfg.el = this.el.createChild();
34558                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34559                 this.add(region, ret);
34560                 break;
34561             */
34562             
34563             case 'Nest': 
34564                 // create a new Layout (which is  a Border Layout...
34565                 
34566                 var clayout = cfg.layout;
34567                 clayout.el  = this.el.createChild();
34568                 clayout.items   = clayout.items  || [];
34569                 
34570                 delete cfg.layout;
34571                 
34572                 // replace this exitems with the clayout ones..
34573                 xitems = clayout.items;
34574                  
34575                 // force background off if it's in center...
34576                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34577                     cfg.background = false;
34578                 }
34579                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34580                 
34581                 
34582                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34583                 //console.log('adding nested layout panel '  + cfg.toSource());
34584                 this.add(region, ret);
34585                 nb = {}; /// find first...
34586                 break;
34587             
34588             case 'Grid':
34589                 
34590                 // needs grid and region
34591                 
34592                 //var el = this.getRegion(region).el.createChild();
34593                 /*
34594                  *var el = this.el.createChild();
34595                 // create the grid first...
34596                 cfg.grid.container = el;
34597                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34598                 */
34599                 
34600                 if (region == 'center' && this.active ) {
34601                     cfg.background = false;
34602                 }
34603                 
34604                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34605                 
34606                 this.add(region, ret);
34607                 /*
34608                 if (cfg.background) {
34609                     // render grid on panel activation (if panel background)
34610                     ret.on('activate', function(gp) {
34611                         if (!gp.grid.rendered) {
34612                     //        gp.grid.render(el);
34613                         }
34614                     });
34615                 } else {
34616                   //  cfg.grid.render(el);
34617                 }
34618                 */
34619                 break;
34620            
34621            
34622             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34623                 // it was the old xcomponent building that caused this before.
34624                 // espeically if border is the top element in the tree.
34625                 ret = this;
34626                 break; 
34627                 
34628                     
34629                 
34630                 
34631                 
34632             default:
34633                 /*
34634                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34635                     
34636                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34637                     this.add(region, ret);
34638                 } else {
34639                 */
34640                     Roo.log(cfg);
34641                     throw "Can not add '" + cfg.xtype + "' to Border";
34642                     return null;
34643              
34644                                 
34645              
34646         }
34647         this.beginUpdate();
34648         // add children..
34649         var region = '';
34650         var abn = {};
34651         Roo.each(xitems, function(i)  {
34652             region = nb && i.region ? i.region : false;
34653             
34654             var add = ret.addxtype(i);
34655            
34656             if (region) {
34657                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34658                 if (!i.background) {
34659                     abn[region] = nb[region] ;
34660                 }
34661             }
34662             
34663         });
34664         this.endUpdate();
34665
34666         // make the last non-background panel active..
34667         //if (nb) { Roo.log(abn); }
34668         if (nb) {
34669             
34670             for(var r in abn) {
34671                 region = this.getRegion(r);
34672                 if (region) {
34673                     // tried using nb[r], but it does not work..
34674                      
34675                     region.showPanel(abn[r]);
34676                    
34677                 }
34678             }
34679         }
34680         return ret;
34681         
34682     },
34683     
34684     
34685 // private
34686     factory : function(cfg)
34687     {
34688         
34689         var validRegions = Roo.bootstrap.layout.Border.regions;
34690
34691         var target = cfg.region;
34692         cfg.mgr = this;
34693         
34694         var r = Roo.bootstrap.layout;
34695         Roo.log(target);
34696         switch(target){
34697             case "north":
34698                 return new r.North(cfg);
34699             case "south":
34700                 return new r.South(cfg);
34701             case "east":
34702                 return new r.East(cfg);
34703             case "west":
34704                 return new r.West(cfg);
34705             case "center":
34706                 return new r.Center(cfg);
34707         }
34708         throw 'Layout region "'+target+'" not supported.';
34709     }
34710     
34711     
34712 });
34713  /*
34714  * Based on:
34715  * Ext JS Library 1.1.1
34716  * Copyright(c) 2006-2007, Ext JS, LLC.
34717  *
34718  * Originally Released Under LGPL - original licence link has changed is not relivant.
34719  *
34720  * Fork - LGPL
34721  * <script type="text/javascript">
34722  */
34723  
34724 /**
34725  * @class Roo.bootstrap.layout.Basic
34726  * @extends Roo.util.Observable
34727  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34728  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34729  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34730  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34731  * @cfg {string}   region  the region that it inhabits..
34732  * @cfg {bool}   skipConfig skip config?
34733  * 
34734
34735  */
34736 Roo.bootstrap.layout.Basic = function(config){
34737     
34738     this.mgr = config.mgr;
34739     
34740     this.position = config.region;
34741     
34742     var skipConfig = config.skipConfig;
34743     
34744     this.events = {
34745         /**
34746          * @scope Roo.BasicLayoutRegion
34747          */
34748         
34749         /**
34750          * @event beforeremove
34751          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34752          * @param {Roo.LayoutRegion} this
34753          * @param {Roo.ContentPanel} panel The panel
34754          * @param {Object} e The cancel event object
34755          */
34756         "beforeremove" : true,
34757         /**
34758          * @event invalidated
34759          * Fires when the layout for this region is changed.
34760          * @param {Roo.LayoutRegion} this
34761          */
34762         "invalidated" : true,
34763         /**
34764          * @event visibilitychange
34765          * Fires when this region is shown or hidden 
34766          * @param {Roo.LayoutRegion} this
34767          * @param {Boolean} visibility true or false
34768          */
34769         "visibilitychange" : true,
34770         /**
34771          * @event paneladded
34772          * Fires when a panel is added. 
34773          * @param {Roo.LayoutRegion} this
34774          * @param {Roo.ContentPanel} panel The panel
34775          */
34776         "paneladded" : true,
34777         /**
34778          * @event panelremoved
34779          * Fires when a panel is removed. 
34780          * @param {Roo.LayoutRegion} this
34781          * @param {Roo.ContentPanel} panel The panel
34782          */
34783         "panelremoved" : true,
34784         /**
34785          * @event beforecollapse
34786          * Fires when this region before collapse.
34787          * @param {Roo.LayoutRegion} this
34788          */
34789         "beforecollapse" : true,
34790         /**
34791          * @event collapsed
34792          * Fires when this region is collapsed.
34793          * @param {Roo.LayoutRegion} this
34794          */
34795         "collapsed" : true,
34796         /**
34797          * @event expanded
34798          * Fires when this region is expanded.
34799          * @param {Roo.LayoutRegion} this
34800          */
34801         "expanded" : true,
34802         /**
34803          * @event slideshow
34804          * Fires when this region is slid into view.
34805          * @param {Roo.LayoutRegion} this
34806          */
34807         "slideshow" : true,
34808         /**
34809          * @event slidehide
34810          * Fires when this region slides out of view. 
34811          * @param {Roo.LayoutRegion} this
34812          */
34813         "slidehide" : true,
34814         /**
34815          * @event panelactivated
34816          * Fires when a panel is activated. 
34817          * @param {Roo.LayoutRegion} this
34818          * @param {Roo.ContentPanel} panel The activated panel
34819          */
34820         "panelactivated" : true,
34821         /**
34822          * @event resized
34823          * Fires when the user resizes this region. 
34824          * @param {Roo.LayoutRegion} this
34825          * @param {Number} newSize The new size (width for east/west, height for north/south)
34826          */
34827         "resized" : true
34828     };
34829     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34830     this.panels = new Roo.util.MixedCollection();
34831     this.panels.getKey = this.getPanelId.createDelegate(this);
34832     this.box = null;
34833     this.activePanel = null;
34834     // ensure listeners are added...
34835     
34836     if (config.listeners || config.events) {
34837         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34838             listeners : config.listeners || {},
34839             events : config.events || {}
34840         });
34841     }
34842     
34843     if(skipConfig !== true){
34844         this.applyConfig(config);
34845     }
34846 };
34847
34848 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34849 {
34850     getPanelId : function(p){
34851         return p.getId();
34852     },
34853     
34854     applyConfig : function(config){
34855         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34856         this.config = config;
34857         
34858     },
34859     
34860     /**
34861      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34862      * the width, for horizontal (north, south) the height.
34863      * @param {Number} newSize The new width or height
34864      */
34865     resizeTo : function(newSize){
34866         var el = this.el ? this.el :
34867                  (this.activePanel ? this.activePanel.getEl() : null);
34868         if(el){
34869             switch(this.position){
34870                 case "east":
34871                 case "west":
34872                     el.setWidth(newSize);
34873                     this.fireEvent("resized", this, newSize);
34874                 break;
34875                 case "north":
34876                 case "south":
34877                     el.setHeight(newSize);
34878                     this.fireEvent("resized", this, newSize);
34879                 break;                
34880             }
34881         }
34882     },
34883     
34884     getBox : function(){
34885         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34886     },
34887     
34888     getMargins : function(){
34889         return this.margins;
34890     },
34891     
34892     updateBox : function(box){
34893         this.box = box;
34894         var el = this.activePanel.getEl();
34895         el.dom.style.left = box.x + "px";
34896         el.dom.style.top = box.y + "px";
34897         this.activePanel.setSize(box.width, box.height);
34898     },
34899     
34900     /**
34901      * Returns the container element for this region.
34902      * @return {Roo.Element}
34903      */
34904     getEl : function(){
34905         return this.activePanel;
34906     },
34907     
34908     /**
34909      * Returns true if this region is currently visible.
34910      * @return {Boolean}
34911      */
34912     isVisible : function(){
34913         return this.activePanel ? true : false;
34914     },
34915     
34916     setActivePanel : function(panel){
34917         panel = this.getPanel(panel);
34918         if(this.activePanel && this.activePanel != panel){
34919             this.activePanel.setActiveState(false);
34920             this.activePanel.getEl().setLeftTop(-10000,-10000);
34921         }
34922         this.activePanel = panel;
34923         panel.setActiveState(true);
34924         if(this.box){
34925             panel.setSize(this.box.width, this.box.height);
34926         }
34927         this.fireEvent("panelactivated", this, panel);
34928         this.fireEvent("invalidated");
34929     },
34930     
34931     /**
34932      * Show the specified panel.
34933      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34934      * @return {Roo.ContentPanel} The shown panel or null
34935      */
34936     showPanel : function(panel){
34937         panel = this.getPanel(panel);
34938         if(panel){
34939             this.setActivePanel(panel);
34940         }
34941         return panel;
34942     },
34943     
34944     /**
34945      * Get the active panel for this region.
34946      * @return {Roo.ContentPanel} The active panel or null
34947      */
34948     getActivePanel : function(){
34949         return this.activePanel;
34950     },
34951     
34952     /**
34953      * Add the passed ContentPanel(s)
34954      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34955      * @return {Roo.ContentPanel} The panel added (if only one was added)
34956      */
34957     add : function(panel){
34958         if(arguments.length > 1){
34959             for(var i = 0, len = arguments.length; i < len; i++) {
34960                 this.add(arguments[i]);
34961             }
34962             return null;
34963         }
34964         if(this.hasPanel(panel)){
34965             this.showPanel(panel);
34966             return panel;
34967         }
34968         var el = panel.getEl();
34969         if(el.dom.parentNode != this.mgr.el.dom){
34970             this.mgr.el.dom.appendChild(el.dom);
34971         }
34972         if(panel.setRegion){
34973             panel.setRegion(this);
34974         }
34975         this.panels.add(panel);
34976         el.setStyle("position", "absolute");
34977         if(!panel.background){
34978             this.setActivePanel(panel);
34979             if(this.config.initialSize && this.panels.getCount()==1){
34980                 this.resizeTo(this.config.initialSize);
34981             }
34982         }
34983         this.fireEvent("paneladded", this, panel);
34984         return panel;
34985     },
34986     
34987     /**
34988      * Returns true if the panel is in this region.
34989      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34990      * @return {Boolean}
34991      */
34992     hasPanel : function(panel){
34993         if(typeof panel == "object"){ // must be panel obj
34994             panel = panel.getId();
34995         }
34996         return this.getPanel(panel) ? true : false;
34997     },
34998     
34999     /**
35000      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35001      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35002      * @param {Boolean} preservePanel Overrides the config preservePanel option
35003      * @return {Roo.ContentPanel} The panel that was removed
35004      */
35005     remove : function(panel, preservePanel){
35006         panel = this.getPanel(panel);
35007         if(!panel){
35008             return null;
35009         }
35010         var e = {};
35011         this.fireEvent("beforeremove", this, panel, e);
35012         if(e.cancel === true){
35013             return null;
35014         }
35015         var panelId = panel.getId();
35016         this.panels.removeKey(panelId);
35017         return panel;
35018     },
35019     
35020     /**
35021      * Returns the panel specified or null if it's not in this region.
35022      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35023      * @return {Roo.ContentPanel}
35024      */
35025     getPanel : function(id){
35026         if(typeof id == "object"){ // must be panel obj
35027             return id;
35028         }
35029         return this.panels.get(id);
35030     },
35031     
35032     /**
35033      * Returns this regions position (north/south/east/west/center).
35034      * @return {String} 
35035      */
35036     getPosition: function(){
35037         return this.position;    
35038     }
35039 });/*
35040  * Based on:
35041  * Ext JS Library 1.1.1
35042  * Copyright(c) 2006-2007, Ext JS, LLC.
35043  *
35044  * Originally Released Under LGPL - original licence link has changed is not relivant.
35045  *
35046  * Fork - LGPL
35047  * <script type="text/javascript">
35048  */
35049  
35050 /**
35051  * @class Roo.bootstrap.layout.Region
35052  * @extends Roo.bootstrap.layout.Basic
35053  * This class represents a region in a layout manager.
35054  
35055  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35056  * @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})
35057  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35058  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35059  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35060  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35061  * @cfg {String}    title           The title for the region (overrides panel titles)
35062  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35063  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35064  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35065  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35066  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35067  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35068  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35069  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35070  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35071  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35072
35073  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35074  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35075  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35076  * @cfg {Number}    width           For East/West panels
35077  * @cfg {Number}    height          For North/South panels
35078  * @cfg {Boolean}   split           To show the splitter
35079  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35080  * 
35081  * @cfg {string}   cls             Extra CSS classes to add to region
35082  * 
35083  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35084  * @cfg {string}   region  the region that it inhabits..
35085  *
35086
35087  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35088  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35089
35090  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35091  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35092  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35093  */
35094 Roo.bootstrap.layout.Region = function(config)
35095 {
35096     this.applyConfig(config);
35097
35098     var mgr = config.mgr;
35099     var pos = config.region;
35100     config.skipConfig = true;
35101     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35102     
35103     if (mgr.el) {
35104         this.onRender(mgr.el);   
35105     }
35106      
35107     this.visible = true;
35108     this.collapsed = false;
35109     this.unrendered_panels = [];
35110 };
35111
35112 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35113
35114     position: '', // set by wrapper (eg. north/south etc..)
35115     unrendered_panels : null,  // unrendered panels.
35116     createBody : function(){
35117         /** This region's body element 
35118         * @type Roo.Element */
35119         this.bodyEl = this.el.createChild({
35120                 tag: "div",
35121                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35122         });
35123     },
35124
35125     onRender: function(ctr, pos)
35126     {
35127         var dh = Roo.DomHelper;
35128         /** This region's container element 
35129         * @type Roo.Element */
35130         this.el = dh.append(ctr.dom, {
35131                 tag: "div",
35132                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35133             }, true);
35134         /** This region's title element 
35135         * @type Roo.Element */
35136     
35137         this.titleEl = dh.append(this.el.dom,
35138             {
35139                     tag: "div",
35140                     unselectable: "on",
35141                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35142                     children:[
35143                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35144                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35145                     ]}, true);
35146         
35147         this.titleEl.enableDisplayMode();
35148         /** This region's title text element 
35149         * @type HTMLElement */
35150         this.titleTextEl = this.titleEl.dom.firstChild;
35151         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35152         /*
35153         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35154         this.closeBtn.enableDisplayMode();
35155         this.closeBtn.on("click", this.closeClicked, this);
35156         this.closeBtn.hide();
35157     */
35158         this.createBody(this.config);
35159         if(this.config.hideWhenEmpty){
35160             this.hide();
35161             this.on("paneladded", this.validateVisibility, this);
35162             this.on("panelremoved", this.validateVisibility, this);
35163         }
35164         if(this.autoScroll){
35165             this.bodyEl.setStyle("overflow", "auto");
35166         }else{
35167             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35168         }
35169         //if(c.titlebar !== false){
35170             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35171                 this.titleEl.hide();
35172             }else{
35173                 this.titleEl.show();
35174                 if(this.config.title){
35175                     this.titleTextEl.innerHTML = this.config.title;
35176                 }
35177             }
35178         //}
35179         if(this.config.collapsed){
35180             this.collapse(true);
35181         }
35182         if(this.config.hidden){
35183             this.hide();
35184         }
35185         
35186         if (this.unrendered_panels && this.unrendered_panels.length) {
35187             for (var i =0;i< this.unrendered_panels.length; i++) {
35188                 this.add(this.unrendered_panels[i]);
35189             }
35190             this.unrendered_panels = null;
35191             
35192         }
35193         
35194     },
35195     
35196     applyConfig : function(c)
35197     {
35198         /*
35199          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35200             var dh = Roo.DomHelper;
35201             if(c.titlebar !== false){
35202                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35203                 this.collapseBtn.on("click", this.collapse, this);
35204                 this.collapseBtn.enableDisplayMode();
35205                 /*
35206                 if(c.showPin === true || this.showPin){
35207                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35208                     this.stickBtn.enableDisplayMode();
35209                     this.stickBtn.on("click", this.expand, this);
35210                     this.stickBtn.hide();
35211                 }
35212                 
35213             }
35214             */
35215             /** This region's collapsed element
35216             * @type Roo.Element */
35217             /*
35218              *
35219             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35220                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35221             ]}, true);
35222             
35223             if(c.floatable !== false){
35224                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35225                this.collapsedEl.on("click", this.collapseClick, this);
35226             }
35227
35228             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35229                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35230                    id: "message", unselectable: "on", style:{"float":"left"}});
35231                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35232              }
35233             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35234             this.expandBtn.on("click", this.expand, this);
35235             
35236         }
35237         
35238         if(this.collapseBtn){
35239             this.collapseBtn.setVisible(c.collapsible == true);
35240         }
35241         
35242         this.cmargins = c.cmargins || this.cmargins ||
35243                          (this.position == "west" || this.position == "east" ?
35244                              {top: 0, left: 2, right:2, bottom: 0} :
35245                              {top: 2, left: 0, right:0, bottom: 2});
35246         */
35247         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35248         
35249         
35250         this.bottomTabs = c.tabPosition != "top";
35251         
35252         this.autoScroll = c.autoScroll || false;
35253         
35254         
35255        
35256         
35257         this.duration = c.duration || .30;
35258         this.slideDuration = c.slideDuration || .45;
35259         this.config = c;
35260        
35261     },
35262     /**
35263      * Returns true if this region is currently visible.
35264      * @return {Boolean}
35265      */
35266     isVisible : function(){
35267         return this.visible;
35268     },
35269
35270     /**
35271      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35272      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35273      */
35274     //setCollapsedTitle : function(title){
35275     //    title = title || "&#160;";
35276      //   if(this.collapsedTitleTextEl){
35277       //      this.collapsedTitleTextEl.innerHTML = title;
35278        // }
35279     //},
35280
35281     getBox : function(){
35282         var b;
35283       //  if(!this.collapsed){
35284             b = this.el.getBox(false, true);
35285        // }else{
35286           //  b = this.collapsedEl.getBox(false, true);
35287         //}
35288         return b;
35289     },
35290
35291     getMargins : function(){
35292         return this.margins;
35293         //return this.collapsed ? this.cmargins : this.margins;
35294     },
35295 /*
35296     highlight : function(){
35297         this.el.addClass("x-layout-panel-dragover");
35298     },
35299
35300     unhighlight : function(){
35301         this.el.removeClass("x-layout-panel-dragover");
35302     },
35303 */
35304     updateBox : function(box)
35305     {
35306         if (!this.bodyEl) {
35307             return; // not rendered yet..
35308         }
35309         
35310         this.box = box;
35311         if(!this.collapsed){
35312             this.el.dom.style.left = box.x + "px";
35313             this.el.dom.style.top = box.y + "px";
35314             this.updateBody(box.width, box.height);
35315         }else{
35316             this.collapsedEl.dom.style.left = box.x + "px";
35317             this.collapsedEl.dom.style.top = box.y + "px";
35318             this.collapsedEl.setSize(box.width, box.height);
35319         }
35320         if(this.tabs){
35321             this.tabs.autoSizeTabs();
35322         }
35323     },
35324
35325     updateBody : function(w, h)
35326     {
35327         if(w !== null){
35328             this.el.setWidth(w);
35329             w -= this.el.getBorderWidth("rl");
35330             if(this.config.adjustments){
35331                 w += this.config.adjustments[0];
35332             }
35333         }
35334         if(h !== null && h > 0){
35335             this.el.setHeight(h);
35336             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35337             h -= this.el.getBorderWidth("tb");
35338             if(this.config.adjustments){
35339                 h += this.config.adjustments[1];
35340             }
35341             this.bodyEl.setHeight(h);
35342             if(this.tabs){
35343                 h = this.tabs.syncHeight(h);
35344             }
35345         }
35346         if(this.panelSize){
35347             w = w !== null ? w : this.panelSize.width;
35348             h = h !== null ? h : this.panelSize.height;
35349         }
35350         if(this.activePanel){
35351             var el = this.activePanel.getEl();
35352             w = w !== null ? w : el.getWidth();
35353             h = h !== null ? h : el.getHeight();
35354             this.panelSize = {width: w, height: h};
35355             this.activePanel.setSize(w, h);
35356         }
35357         if(Roo.isIE && this.tabs){
35358             this.tabs.el.repaint();
35359         }
35360     },
35361
35362     /**
35363      * Returns the container element for this region.
35364      * @return {Roo.Element}
35365      */
35366     getEl : function(){
35367         return this.el;
35368     },
35369
35370     /**
35371      * Hides this region.
35372      */
35373     hide : function(){
35374         //if(!this.collapsed){
35375             this.el.dom.style.left = "-2000px";
35376             this.el.hide();
35377         //}else{
35378          //   this.collapsedEl.dom.style.left = "-2000px";
35379          //   this.collapsedEl.hide();
35380        // }
35381         this.visible = false;
35382         this.fireEvent("visibilitychange", this, false);
35383     },
35384
35385     /**
35386      * Shows this region if it was previously hidden.
35387      */
35388     show : function(){
35389         //if(!this.collapsed){
35390             this.el.show();
35391         //}else{
35392         //    this.collapsedEl.show();
35393        // }
35394         this.visible = true;
35395         this.fireEvent("visibilitychange", this, true);
35396     },
35397 /*
35398     closeClicked : function(){
35399         if(this.activePanel){
35400             this.remove(this.activePanel);
35401         }
35402     },
35403
35404     collapseClick : function(e){
35405         if(this.isSlid){
35406            e.stopPropagation();
35407            this.slideIn();
35408         }else{
35409            e.stopPropagation();
35410            this.slideOut();
35411         }
35412     },
35413 */
35414     /**
35415      * Collapses this region.
35416      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35417      */
35418     /*
35419     collapse : function(skipAnim, skipCheck = false){
35420         if(this.collapsed) {
35421             return;
35422         }
35423         
35424         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35425             
35426             this.collapsed = true;
35427             if(this.split){
35428                 this.split.el.hide();
35429             }
35430             if(this.config.animate && skipAnim !== true){
35431                 this.fireEvent("invalidated", this);
35432                 this.animateCollapse();
35433             }else{
35434                 this.el.setLocation(-20000,-20000);
35435                 this.el.hide();
35436                 this.collapsedEl.show();
35437                 this.fireEvent("collapsed", this);
35438                 this.fireEvent("invalidated", this);
35439             }
35440         }
35441         
35442     },
35443 */
35444     animateCollapse : function(){
35445         // overridden
35446     },
35447
35448     /**
35449      * Expands this region if it was previously collapsed.
35450      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35451      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35452      */
35453     /*
35454     expand : function(e, skipAnim){
35455         if(e) {
35456             e.stopPropagation();
35457         }
35458         if(!this.collapsed || this.el.hasActiveFx()) {
35459             return;
35460         }
35461         if(this.isSlid){
35462             this.afterSlideIn();
35463             skipAnim = true;
35464         }
35465         this.collapsed = false;
35466         if(this.config.animate && skipAnim !== true){
35467             this.animateExpand();
35468         }else{
35469             this.el.show();
35470             if(this.split){
35471                 this.split.el.show();
35472             }
35473             this.collapsedEl.setLocation(-2000,-2000);
35474             this.collapsedEl.hide();
35475             this.fireEvent("invalidated", this);
35476             this.fireEvent("expanded", this);
35477         }
35478     },
35479 */
35480     animateExpand : function(){
35481         // overridden
35482     },
35483
35484     initTabs : function()
35485     {
35486         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35487         
35488         var ts = new Roo.bootstrap.panel.Tabs({
35489                 el: this.bodyEl.dom,
35490                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35491                 disableTooltips: this.config.disableTabTips,
35492                 toolbar : this.config.toolbar
35493             });
35494         
35495         if(this.config.hideTabs){
35496             ts.stripWrap.setDisplayed(false);
35497         }
35498         this.tabs = ts;
35499         ts.resizeTabs = this.config.resizeTabs === true;
35500         ts.minTabWidth = this.config.minTabWidth || 40;
35501         ts.maxTabWidth = this.config.maxTabWidth || 250;
35502         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35503         ts.monitorResize = false;
35504         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35505         ts.bodyEl.addClass('roo-layout-tabs-body');
35506         this.panels.each(this.initPanelAsTab, this);
35507     },
35508
35509     initPanelAsTab : function(panel){
35510         var ti = this.tabs.addTab(
35511             panel.getEl().id,
35512             panel.getTitle(),
35513             null,
35514             this.config.closeOnTab && panel.isClosable(),
35515             panel.tpl
35516         );
35517         if(panel.tabTip !== undefined){
35518             ti.setTooltip(panel.tabTip);
35519         }
35520         ti.on("activate", function(){
35521               this.setActivePanel(panel);
35522         }, this);
35523         
35524         if(this.config.closeOnTab){
35525             ti.on("beforeclose", function(t, e){
35526                 e.cancel = true;
35527                 this.remove(panel);
35528             }, this);
35529         }
35530         
35531         panel.tabItem = ti;
35532         
35533         return ti;
35534     },
35535
35536     updatePanelTitle : function(panel, title)
35537     {
35538         if(this.activePanel == panel){
35539             this.updateTitle(title);
35540         }
35541         if(this.tabs){
35542             var ti = this.tabs.getTab(panel.getEl().id);
35543             ti.setText(title);
35544             if(panel.tabTip !== undefined){
35545                 ti.setTooltip(panel.tabTip);
35546             }
35547         }
35548     },
35549
35550     updateTitle : function(title){
35551         if(this.titleTextEl && !this.config.title){
35552             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35553         }
35554     },
35555
35556     setActivePanel : function(panel)
35557     {
35558         panel = this.getPanel(panel);
35559         if(this.activePanel && this.activePanel != panel){
35560             if(this.activePanel.setActiveState(false) === false){
35561                 return;
35562             }
35563         }
35564         this.activePanel = panel;
35565         panel.setActiveState(true);
35566         if(this.panelSize){
35567             panel.setSize(this.panelSize.width, this.panelSize.height);
35568         }
35569         if(this.closeBtn){
35570             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35571         }
35572         this.updateTitle(panel.getTitle());
35573         if(this.tabs){
35574             this.fireEvent("invalidated", this);
35575         }
35576         this.fireEvent("panelactivated", this, panel);
35577     },
35578
35579     /**
35580      * Shows the specified panel.
35581      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35582      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35583      */
35584     showPanel : function(panel)
35585     {
35586         panel = this.getPanel(panel);
35587         if(panel){
35588             if(this.tabs){
35589                 var tab = this.tabs.getTab(panel.getEl().id);
35590                 if(tab.isHidden()){
35591                     this.tabs.unhideTab(tab.id);
35592                 }
35593                 tab.activate();
35594             }else{
35595                 this.setActivePanel(panel);
35596             }
35597         }
35598         return panel;
35599     },
35600
35601     /**
35602      * Get the active panel for this region.
35603      * @return {Roo.ContentPanel} The active panel or null
35604      */
35605     getActivePanel : function(){
35606         return this.activePanel;
35607     },
35608
35609     validateVisibility : function(){
35610         if(this.panels.getCount() < 1){
35611             this.updateTitle("&#160;");
35612             this.closeBtn.hide();
35613             this.hide();
35614         }else{
35615             if(!this.isVisible()){
35616                 this.show();
35617             }
35618         }
35619     },
35620
35621     /**
35622      * Adds the passed ContentPanel(s) to this region.
35623      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35624      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35625      */
35626     add : function(panel)
35627     {
35628         if(arguments.length > 1){
35629             for(var i = 0, len = arguments.length; i < len; i++) {
35630                 this.add(arguments[i]);
35631             }
35632             return null;
35633         }
35634         
35635         // if we have not been rendered yet, then we can not really do much of this..
35636         if (!this.bodyEl) {
35637             this.unrendered_panels.push(panel);
35638             return panel;
35639         }
35640         
35641         
35642         
35643         
35644         if(this.hasPanel(panel)){
35645             this.showPanel(panel);
35646             return panel;
35647         }
35648         panel.setRegion(this);
35649         this.panels.add(panel);
35650        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35651             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35652             // and hide them... ???
35653             this.bodyEl.dom.appendChild(panel.getEl().dom);
35654             if(panel.background !== true){
35655                 this.setActivePanel(panel);
35656             }
35657             this.fireEvent("paneladded", this, panel);
35658             return panel;
35659         }
35660         */
35661         if(!this.tabs){
35662             this.initTabs();
35663         }else{
35664             this.initPanelAsTab(panel);
35665         }
35666         
35667         
35668         if(panel.background !== true){
35669             this.tabs.activate(panel.getEl().id);
35670         }
35671         this.fireEvent("paneladded", this, panel);
35672         return panel;
35673     },
35674
35675     /**
35676      * Hides the tab for the specified panel.
35677      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35678      */
35679     hidePanel : function(panel){
35680         if(this.tabs && (panel = this.getPanel(panel))){
35681             this.tabs.hideTab(panel.getEl().id);
35682         }
35683     },
35684
35685     /**
35686      * Unhides the tab for a previously hidden panel.
35687      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35688      */
35689     unhidePanel : function(panel){
35690         if(this.tabs && (panel = this.getPanel(panel))){
35691             this.tabs.unhideTab(panel.getEl().id);
35692         }
35693     },
35694
35695     clearPanels : function(){
35696         while(this.panels.getCount() > 0){
35697              this.remove(this.panels.first());
35698         }
35699     },
35700
35701     /**
35702      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35703      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35704      * @param {Boolean} preservePanel Overrides the config preservePanel option
35705      * @return {Roo.ContentPanel} The panel that was removed
35706      */
35707     remove : function(panel, preservePanel)
35708     {
35709         panel = this.getPanel(panel);
35710         if(!panel){
35711             return null;
35712         }
35713         var e = {};
35714         this.fireEvent("beforeremove", this, panel, e);
35715         if(e.cancel === true){
35716             return null;
35717         }
35718         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35719         var panelId = panel.getId();
35720         this.panels.removeKey(panelId);
35721         if(preservePanel){
35722             document.body.appendChild(panel.getEl().dom);
35723         }
35724         if(this.tabs){
35725             this.tabs.removeTab(panel.getEl().id);
35726         }else if (!preservePanel){
35727             this.bodyEl.dom.removeChild(panel.getEl().dom);
35728         }
35729         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35730             var p = this.panels.first();
35731             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35732             tempEl.appendChild(p.getEl().dom);
35733             this.bodyEl.update("");
35734             this.bodyEl.dom.appendChild(p.getEl().dom);
35735             tempEl = null;
35736             this.updateTitle(p.getTitle());
35737             this.tabs = null;
35738             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35739             this.setActivePanel(p);
35740         }
35741         panel.setRegion(null);
35742         if(this.activePanel == panel){
35743             this.activePanel = null;
35744         }
35745         if(this.config.autoDestroy !== false && preservePanel !== true){
35746             try{panel.destroy();}catch(e){}
35747         }
35748         this.fireEvent("panelremoved", this, panel);
35749         return panel;
35750     },
35751
35752     /**
35753      * Returns the TabPanel component used by this region
35754      * @return {Roo.TabPanel}
35755      */
35756     getTabs : function(){
35757         return this.tabs;
35758     },
35759
35760     createTool : function(parentEl, className){
35761         var btn = Roo.DomHelper.append(parentEl, {
35762             tag: "div",
35763             cls: "x-layout-tools-button",
35764             children: [ {
35765                 tag: "div",
35766                 cls: "roo-layout-tools-button-inner " + className,
35767                 html: "&#160;"
35768             }]
35769         }, true);
35770         btn.addClassOnOver("roo-layout-tools-button-over");
35771         return btn;
35772     }
35773 });/*
35774  * Based on:
35775  * Ext JS Library 1.1.1
35776  * Copyright(c) 2006-2007, Ext JS, LLC.
35777  *
35778  * Originally Released Under LGPL - original licence link has changed is not relivant.
35779  *
35780  * Fork - LGPL
35781  * <script type="text/javascript">
35782  */
35783  
35784
35785
35786 /**
35787  * @class Roo.SplitLayoutRegion
35788  * @extends Roo.LayoutRegion
35789  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35790  */
35791 Roo.bootstrap.layout.Split = function(config){
35792     this.cursor = config.cursor;
35793     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35794 };
35795
35796 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35797 {
35798     splitTip : "Drag to resize.",
35799     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35800     useSplitTips : false,
35801
35802     applyConfig : function(config){
35803         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35804     },
35805     
35806     onRender : function(ctr,pos) {
35807         
35808         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35809         if(!this.config.split){
35810             return;
35811         }
35812         if(!this.split){
35813             
35814             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35815                             tag: "div",
35816                             id: this.el.id + "-split",
35817                             cls: "roo-layout-split roo-layout-split-"+this.position,
35818                             html: "&#160;"
35819             });
35820             /** The SplitBar for this region 
35821             * @type Roo.SplitBar */
35822             // does not exist yet...
35823             Roo.log([this.position, this.orientation]);
35824             
35825             this.split = new Roo.bootstrap.SplitBar({
35826                 dragElement : splitEl,
35827                 resizingElement: this.el,
35828                 orientation : this.orientation
35829             });
35830             
35831             this.split.on("moved", this.onSplitMove, this);
35832             this.split.useShim = this.config.useShim === true;
35833             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35834             if(this.useSplitTips){
35835                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35836             }
35837             //if(config.collapsible){
35838             //    this.split.el.on("dblclick", this.collapse,  this);
35839             //}
35840         }
35841         if(typeof this.config.minSize != "undefined"){
35842             this.split.minSize = this.config.minSize;
35843         }
35844         if(typeof this.config.maxSize != "undefined"){
35845             this.split.maxSize = this.config.maxSize;
35846         }
35847         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35848             this.hideSplitter();
35849         }
35850         
35851     },
35852
35853     getHMaxSize : function(){
35854          var cmax = this.config.maxSize || 10000;
35855          var center = this.mgr.getRegion("center");
35856          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35857     },
35858
35859     getVMaxSize : function(){
35860          var cmax = this.config.maxSize || 10000;
35861          var center = this.mgr.getRegion("center");
35862          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35863     },
35864
35865     onSplitMove : function(split, newSize){
35866         this.fireEvent("resized", this, newSize);
35867     },
35868     
35869     /** 
35870      * Returns the {@link Roo.SplitBar} for this region.
35871      * @return {Roo.SplitBar}
35872      */
35873     getSplitBar : function(){
35874         return this.split;
35875     },
35876     
35877     hide : function(){
35878         this.hideSplitter();
35879         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35880     },
35881
35882     hideSplitter : function(){
35883         if(this.split){
35884             this.split.el.setLocation(-2000,-2000);
35885             this.split.el.hide();
35886         }
35887     },
35888
35889     show : function(){
35890         if(this.split){
35891             this.split.el.show();
35892         }
35893         Roo.bootstrap.layout.Split.superclass.show.call(this);
35894     },
35895     
35896     beforeSlide: function(){
35897         if(Roo.isGecko){// firefox overflow auto bug workaround
35898             this.bodyEl.clip();
35899             if(this.tabs) {
35900                 this.tabs.bodyEl.clip();
35901             }
35902             if(this.activePanel){
35903                 this.activePanel.getEl().clip();
35904                 
35905                 if(this.activePanel.beforeSlide){
35906                     this.activePanel.beforeSlide();
35907                 }
35908             }
35909         }
35910     },
35911     
35912     afterSlide : function(){
35913         if(Roo.isGecko){// firefox overflow auto bug workaround
35914             this.bodyEl.unclip();
35915             if(this.tabs) {
35916                 this.tabs.bodyEl.unclip();
35917             }
35918             if(this.activePanel){
35919                 this.activePanel.getEl().unclip();
35920                 if(this.activePanel.afterSlide){
35921                     this.activePanel.afterSlide();
35922                 }
35923             }
35924         }
35925     },
35926
35927     initAutoHide : function(){
35928         if(this.autoHide !== false){
35929             if(!this.autoHideHd){
35930                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35931                 this.autoHideHd = {
35932                     "mouseout": function(e){
35933                         if(!e.within(this.el, true)){
35934                             st.delay(500);
35935                         }
35936                     },
35937                     "mouseover" : function(e){
35938                         st.cancel();
35939                     },
35940                     scope : this
35941                 };
35942             }
35943             this.el.on(this.autoHideHd);
35944         }
35945     },
35946
35947     clearAutoHide : function(){
35948         if(this.autoHide !== false){
35949             this.el.un("mouseout", this.autoHideHd.mouseout);
35950             this.el.un("mouseover", this.autoHideHd.mouseover);
35951         }
35952     },
35953
35954     clearMonitor : function(){
35955         Roo.get(document).un("click", this.slideInIf, this);
35956     },
35957
35958     // these names are backwards but not changed for compat
35959     slideOut : function(){
35960         if(this.isSlid || this.el.hasActiveFx()){
35961             return;
35962         }
35963         this.isSlid = true;
35964         if(this.collapseBtn){
35965             this.collapseBtn.hide();
35966         }
35967         this.closeBtnState = this.closeBtn.getStyle('display');
35968         this.closeBtn.hide();
35969         if(this.stickBtn){
35970             this.stickBtn.show();
35971         }
35972         this.el.show();
35973         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35974         this.beforeSlide();
35975         this.el.setStyle("z-index", 10001);
35976         this.el.slideIn(this.getSlideAnchor(), {
35977             callback: function(){
35978                 this.afterSlide();
35979                 this.initAutoHide();
35980                 Roo.get(document).on("click", this.slideInIf, this);
35981                 this.fireEvent("slideshow", this);
35982             },
35983             scope: this,
35984             block: true
35985         });
35986     },
35987
35988     afterSlideIn : function(){
35989         this.clearAutoHide();
35990         this.isSlid = false;
35991         this.clearMonitor();
35992         this.el.setStyle("z-index", "");
35993         if(this.collapseBtn){
35994             this.collapseBtn.show();
35995         }
35996         this.closeBtn.setStyle('display', this.closeBtnState);
35997         if(this.stickBtn){
35998             this.stickBtn.hide();
35999         }
36000         this.fireEvent("slidehide", this);
36001     },
36002
36003     slideIn : function(cb){
36004         if(!this.isSlid || this.el.hasActiveFx()){
36005             Roo.callback(cb);
36006             return;
36007         }
36008         this.isSlid = false;
36009         this.beforeSlide();
36010         this.el.slideOut(this.getSlideAnchor(), {
36011             callback: function(){
36012                 this.el.setLeftTop(-10000, -10000);
36013                 this.afterSlide();
36014                 this.afterSlideIn();
36015                 Roo.callback(cb);
36016             },
36017             scope: this,
36018             block: true
36019         });
36020     },
36021     
36022     slideInIf : function(e){
36023         if(!e.within(this.el)){
36024             this.slideIn();
36025         }
36026     },
36027
36028     animateCollapse : function(){
36029         this.beforeSlide();
36030         this.el.setStyle("z-index", 20000);
36031         var anchor = this.getSlideAnchor();
36032         this.el.slideOut(anchor, {
36033             callback : function(){
36034                 this.el.setStyle("z-index", "");
36035                 this.collapsedEl.slideIn(anchor, {duration:.3});
36036                 this.afterSlide();
36037                 this.el.setLocation(-10000,-10000);
36038                 this.el.hide();
36039                 this.fireEvent("collapsed", this);
36040             },
36041             scope: this,
36042             block: true
36043         });
36044     },
36045
36046     animateExpand : function(){
36047         this.beforeSlide();
36048         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36049         this.el.setStyle("z-index", 20000);
36050         this.collapsedEl.hide({
36051             duration:.1
36052         });
36053         this.el.slideIn(this.getSlideAnchor(), {
36054             callback : function(){
36055                 this.el.setStyle("z-index", "");
36056                 this.afterSlide();
36057                 if(this.split){
36058                     this.split.el.show();
36059                 }
36060                 this.fireEvent("invalidated", this);
36061                 this.fireEvent("expanded", this);
36062             },
36063             scope: this,
36064             block: true
36065         });
36066     },
36067
36068     anchors : {
36069         "west" : "left",
36070         "east" : "right",
36071         "north" : "top",
36072         "south" : "bottom"
36073     },
36074
36075     sanchors : {
36076         "west" : "l",
36077         "east" : "r",
36078         "north" : "t",
36079         "south" : "b"
36080     },
36081
36082     canchors : {
36083         "west" : "tl-tr",
36084         "east" : "tr-tl",
36085         "north" : "tl-bl",
36086         "south" : "bl-tl"
36087     },
36088
36089     getAnchor : function(){
36090         return this.anchors[this.position];
36091     },
36092
36093     getCollapseAnchor : function(){
36094         return this.canchors[this.position];
36095     },
36096
36097     getSlideAnchor : function(){
36098         return this.sanchors[this.position];
36099     },
36100
36101     getAlignAdj : function(){
36102         var cm = this.cmargins;
36103         switch(this.position){
36104             case "west":
36105                 return [0, 0];
36106             break;
36107             case "east":
36108                 return [0, 0];
36109             break;
36110             case "north":
36111                 return [0, 0];
36112             break;
36113             case "south":
36114                 return [0, 0];
36115             break;
36116         }
36117     },
36118
36119     getExpandAdj : function(){
36120         var c = this.collapsedEl, cm = this.cmargins;
36121         switch(this.position){
36122             case "west":
36123                 return [-(cm.right+c.getWidth()+cm.left), 0];
36124             break;
36125             case "east":
36126                 return [cm.right+c.getWidth()+cm.left, 0];
36127             break;
36128             case "north":
36129                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36130             break;
36131             case "south":
36132                 return [0, cm.top+cm.bottom+c.getHeight()];
36133             break;
36134         }
36135     }
36136 });/*
36137  * Based on:
36138  * Ext JS Library 1.1.1
36139  * Copyright(c) 2006-2007, Ext JS, LLC.
36140  *
36141  * Originally Released Under LGPL - original licence link has changed is not relivant.
36142  *
36143  * Fork - LGPL
36144  * <script type="text/javascript">
36145  */
36146 /*
36147  * These classes are private internal classes
36148  */
36149 Roo.bootstrap.layout.Center = function(config){
36150     config.region = "center";
36151     Roo.bootstrap.layout.Region.call(this, config);
36152     this.visible = true;
36153     this.minWidth = config.minWidth || 20;
36154     this.minHeight = config.minHeight || 20;
36155 };
36156
36157 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36158     hide : function(){
36159         // center panel can't be hidden
36160     },
36161     
36162     show : function(){
36163         // center panel can't be hidden
36164     },
36165     
36166     getMinWidth: function(){
36167         return this.minWidth;
36168     },
36169     
36170     getMinHeight: function(){
36171         return this.minHeight;
36172     }
36173 });
36174
36175
36176
36177
36178  
36179
36180
36181
36182
36183
36184 Roo.bootstrap.layout.North = function(config)
36185 {
36186     config.region = 'north';
36187     config.cursor = 'n-resize';
36188     
36189     Roo.bootstrap.layout.Split.call(this, config);
36190     
36191     
36192     if(this.split){
36193         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36194         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36195         this.split.el.addClass("roo-layout-split-v");
36196     }
36197     var size = config.initialSize || config.height;
36198     if(typeof size != "undefined"){
36199         this.el.setHeight(size);
36200     }
36201 };
36202 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36203 {
36204     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36205     
36206     
36207     
36208     getBox : function(){
36209         if(this.collapsed){
36210             return this.collapsedEl.getBox();
36211         }
36212         var box = this.el.getBox();
36213         if(this.split){
36214             box.height += this.split.el.getHeight();
36215         }
36216         return box;
36217     },
36218     
36219     updateBox : function(box){
36220         if(this.split && !this.collapsed){
36221             box.height -= this.split.el.getHeight();
36222             this.split.el.setLeft(box.x);
36223             this.split.el.setTop(box.y+box.height);
36224             this.split.el.setWidth(box.width);
36225         }
36226         if(this.collapsed){
36227             this.updateBody(box.width, null);
36228         }
36229         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36230     }
36231 });
36232
36233
36234
36235
36236
36237 Roo.bootstrap.layout.South = function(config){
36238     config.region = 'south';
36239     config.cursor = 's-resize';
36240     Roo.bootstrap.layout.Split.call(this, config);
36241     if(this.split){
36242         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36243         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36244         this.split.el.addClass("roo-layout-split-v");
36245     }
36246     var size = config.initialSize || config.height;
36247     if(typeof size != "undefined"){
36248         this.el.setHeight(size);
36249     }
36250 };
36251
36252 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36253     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36254     getBox : function(){
36255         if(this.collapsed){
36256             return this.collapsedEl.getBox();
36257         }
36258         var box = this.el.getBox();
36259         if(this.split){
36260             var sh = this.split.el.getHeight();
36261             box.height += sh;
36262             box.y -= sh;
36263         }
36264         return box;
36265     },
36266     
36267     updateBox : function(box){
36268         if(this.split && !this.collapsed){
36269             var sh = this.split.el.getHeight();
36270             box.height -= sh;
36271             box.y += sh;
36272             this.split.el.setLeft(box.x);
36273             this.split.el.setTop(box.y-sh);
36274             this.split.el.setWidth(box.width);
36275         }
36276         if(this.collapsed){
36277             this.updateBody(box.width, null);
36278         }
36279         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36280     }
36281 });
36282
36283 Roo.bootstrap.layout.East = function(config){
36284     config.region = "east";
36285     config.cursor = "e-resize";
36286     Roo.bootstrap.layout.Split.call(this, config);
36287     if(this.split){
36288         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36289         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36290         this.split.el.addClass("roo-layout-split-h");
36291     }
36292     var size = config.initialSize || config.width;
36293     if(typeof size != "undefined"){
36294         this.el.setWidth(size);
36295     }
36296 };
36297 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36298     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36299     getBox : function(){
36300         if(this.collapsed){
36301             return this.collapsedEl.getBox();
36302         }
36303         var box = this.el.getBox();
36304         if(this.split){
36305             var sw = this.split.el.getWidth();
36306             box.width += sw;
36307             box.x -= sw;
36308         }
36309         return box;
36310     },
36311
36312     updateBox : function(box){
36313         if(this.split && !this.collapsed){
36314             var sw = this.split.el.getWidth();
36315             box.width -= sw;
36316             this.split.el.setLeft(box.x);
36317             this.split.el.setTop(box.y);
36318             this.split.el.setHeight(box.height);
36319             box.x += sw;
36320         }
36321         if(this.collapsed){
36322             this.updateBody(null, box.height);
36323         }
36324         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36325     }
36326 });
36327
36328 Roo.bootstrap.layout.West = function(config){
36329     config.region = "west";
36330     config.cursor = "w-resize";
36331     
36332     Roo.bootstrap.layout.Split.call(this, config);
36333     if(this.split){
36334         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36335         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36336         this.split.el.addClass("roo-layout-split-h");
36337     }
36338     
36339 };
36340 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36341     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36342     
36343     onRender: function(ctr, pos)
36344     {
36345         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36346         var size = this.config.initialSize || this.config.width;
36347         if(typeof size != "undefined"){
36348             this.el.setWidth(size);
36349         }
36350     },
36351     
36352     getBox : function(){
36353         if(this.collapsed){
36354             return this.collapsedEl.getBox();
36355         }
36356         var box = this.el.getBox();
36357         if(this.split){
36358             box.width += this.split.el.getWidth();
36359         }
36360         return box;
36361     },
36362     
36363     updateBox : function(box){
36364         if(this.split && !this.collapsed){
36365             var sw = this.split.el.getWidth();
36366             box.width -= sw;
36367             this.split.el.setLeft(box.x+box.width);
36368             this.split.el.setTop(box.y);
36369             this.split.el.setHeight(box.height);
36370         }
36371         if(this.collapsed){
36372             this.updateBody(null, box.height);
36373         }
36374         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36375     }
36376 });
36377 Roo.namespace("Roo.bootstrap.panel");/*
36378  * Based on:
36379  * Ext JS Library 1.1.1
36380  * Copyright(c) 2006-2007, Ext JS, LLC.
36381  *
36382  * Originally Released Under LGPL - original licence link has changed is not relivant.
36383  *
36384  * Fork - LGPL
36385  * <script type="text/javascript">
36386  */
36387 /**
36388  * @class Roo.ContentPanel
36389  * @extends Roo.util.Observable
36390  * A basic ContentPanel element.
36391  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36392  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36393  * @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
36394  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36395  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36396  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36397  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36398  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36399  * @cfg {String} title          The title for this panel
36400  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36401  * @cfg {String} url            Calls {@link #setUrl} with this value
36402  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36403  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36404  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36405  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36406  * @cfg {Boolean} badges render the badges
36407
36408  * @constructor
36409  * Create a new ContentPanel.
36410  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36411  * @param {String/Object} config A string to set only the title or a config object
36412  * @param {String} content (optional) Set the HTML content for this panel
36413  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36414  */
36415 Roo.bootstrap.panel.Content = function( config){
36416     
36417     this.tpl = config.tpl || false;
36418     
36419     var el = config.el;
36420     var content = config.content;
36421
36422     if(config.autoCreate){ // xtype is available if this is called from factory
36423         el = Roo.id();
36424     }
36425     this.el = Roo.get(el);
36426     if(!this.el && config && config.autoCreate){
36427         if(typeof config.autoCreate == "object"){
36428             if(!config.autoCreate.id){
36429                 config.autoCreate.id = config.id||el;
36430             }
36431             this.el = Roo.DomHelper.append(document.body,
36432                         config.autoCreate, true);
36433         }else{
36434             var elcfg =  {   tag: "div",
36435                             cls: "roo-layout-inactive-content",
36436                             id: config.id||el
36437                             };
36438             if (config.html) {
36439                 elcfg.html = config.html;
36440                 
36441             }
36442                         
36443             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36444         }
36445     } 
36446     this.closable = false;
36447     this.loaded = false;
36448     this.active = false;
36449    
36450       
36451     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36452         
36453         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36454         
36455         this.wrapEl = this.el; //this.el.wrap();
36456         var ti = [];
36457         if (config.toolbar.items) {
36458             ti = config.toolbar.items ;
36459             delete config.toolbar.items ;
36460         }
36461         
36462         var nitems = [];
36463         this.toolbar.render(this.wrapEl, 'before');
36464         for(var i =0;i < ti.length;i++) {
36465           //  Roo.log(['add child', items[i]]);
36466             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36467         }
36468         this.toolbar.items = nitems;
36469         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36470         delete config.toolbar;
36471         
36472     }
36473     /*
36474     // xtype created footer. - not sure if will work as we normally have to render first..
36475     if (this.footer && !this.footer.el && this.footer.xtype) {
36476         if (!this.wrapEl) {
36477             this.wrapEl = this.el.wrap();
36478         }
36479     
36480         this.footer.container = this.wrapEl.createChild();
36481          
36482         this.footer = Roo.factory(this.footer, Roo);
36483         
36484     }
36485     */
36486     
36487      if(typeof config == "string"){
36488         this.title = config;
36489     }else{
36490         Roo.apply(this, config);
36491     }
36492     
36493     if(this.resizeEl){
36494         this.resizeEl = Roo.get(this.resizeEl, true);
36495     }else{
36496         this.resizeEl = this.el;
36497     }
36498     // handle view.xtype
36499     
36500  
36501     
36502     
36503     this.addEvents({
36504         /**
36505          * @event activate
36506          * Fires when this panel is activated. 
36507          * @param {Roo.ContentPanel} this
36508          */
36509         "activate" : true,
36510         /**
36511          * @event deactivate
36512          * Fires when this panel is activated. 
36513          * @param {Roo.ContentPanel} this
36514          */
36515         "deactivate" : true,
36516
36517         /**
36518          * @event resize
36519          * Fires when this panel is resized if fitToFrame is true.
36520          * @param {Roo.ContentPanel} this
36521          * @param {Number} width The width after any component adjustments
36522          * @param {Number} height The height after any component adjustments
36523          */
36524         "resize" : true,
36525         
36526          /**
36527          * @event render
36528          * Fires when this tab is created
36529          * @param {Roo.ContentPanel} this
36530          */
36531         "render" : true
36532         
36533         
36534         
36535     });
36536     
36537
36538     
36539     
36540     if(this.autoScroll){
36541         this.resizeEl.setStyle("overflow", "auto");
36542     } else {
36543         // fix randome scrolling
36544         //this.el.on('scroll', function() {
36545         //    Roo.log('fix random scolling');
36546         //    this.scrollTo('top',0); 
36547         //});
36548     }
36549     content = content || this.content;
36550     if(content){
36551         this.setContent(content);
36552     }
36553     if(config && config.url){
36554         this.setUrl(this.url, this.params, this.loadOnce);
36555     }
36556     
36557     
36558     
36559     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36560     
36561     if (this.view && typeof(this.view.xtype) != 'undefined') {
36562         this.view.el = this.el.appendChild(document.createElement("div"));
36563         this.view = Roo.factory(this.view); 
36564         this.view.render  &&  this.view.render(false, '');  
36565     }
36566     
36567     
36568     this.fireEvent('render', this);
36569 };
36570
36571 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36572     
36573     tabTip : '',
36574     
36575     setRegion : function(region){
36576         this.region = region;
36577         this.setActiveClass(region && !this.background);
36578     },
36579     
36580     
36581     setActiveClass: function(state)
36582     {
36583         if(state){
36584            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36585            this.el.setStyle('position','relative');
36586         }else{
36587            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36588            this.el.setStyle('position', 'absolute');
36589         } 
36590     },
36591     
36592     /**
36593      * Returns the toolbar for this Panel if one was configured. 
36594      * @return {Roo.Toolbar} 
36595      */
36596     getToolbar : function(){
36597         return this.toolbar;
36598     },
36599     
36600     setActiveState : function(active)
36601     {
36602         this.active = active;
36603         this.setActiveClass(active);
36604         if(!active){
36605             if(this.fireEvent("deactivate", this) === false){
36606                 return false;
36607             }
36608             return true;
36609         }
36610         this.fireEvent("activate", this);
36611         return true;
36612     },
36613     /**
36614      * Updates this panel's element
36615      * @param {String} content The new content
36616      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36617     */
36618     setContent : function(content, loadScripts){
36619         this.el.update(content, loadScripts);
36620     },
36621
36622     ignoreResize : function(w, h){
36623         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36624             return true;
36625         }else{
36626             this.lastSize = {width: w, height: h};
36627             return false;
36628         }
36629     },
36630     /**
36631      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36632      * @return {Roo.UpdateManager} The UpdateManager
36633      */
36634     getUpdateManager : function(){
36635         return this.el.getUpdateManager();
36636     },
36637      /**
36638      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36639      * @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:
36640 <pre><code>
36641 panel.load({
36642     url: "your-url.php",
36643     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36644     callback: yourFunction,
36645     scope: yourObject, //(optional scope)
36646     discardUrl: false,
36647     nocache: false,
36648     text: "Loading...",
36649     timeout: 30,
36650     scripts: false
36651 });
36652 </code></pre>
36653      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36654      * 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.
36655      * @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}
36656      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36657      * @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.
36658      * @return {Roo.ContentPanel} this
36659      */
36660     load : function(){
36661         var um = this.el.getUpdateManager();
36662         um.update.apply(um, arguments);
36663         return this;
36664     },
36665
36666
36667     /**
36668      * 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.
36669      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36670      * @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)
36671      * @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)
36672      * @return {Roo.UpdateManager} The UpdateManager
36673      */
36674     setUrl : function(url, params, loadOnce){
36675         if(this.refreshDelegate){
36676             this.removeListener("activate", this.refreshDelegate);
36677         }
36678         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36679         this.on("activate", this.refreshDelegate);
36680         return this.el.getUpdateManager();
36681     },
36682     
36683     _handleRefresh : function(url, params, loadOnce){
36684         if(!loadOnce || !this.loaded){
36685             var updater = this.el.getUpdateManager();
36686             updater.update(url, params, this._setLoaded.createDelegate(this));
36687         }
36688     },
36689     
36690     _setLoaded : function(){
36691         this.loaded = true;
36692     }, 
36693     
36694     /**
36695      * Returns this panel's id
36696      * @return {String} 
36697      */
36698     getId : function(){
36699         return this.el.id;
36700     },
36701     
36702     /** 
36703      * Returns this panel's element - used by regiosn to add.
36704      * @return {Roo.Element} 
36705      */
36706     getEl : function(){
36707         return this.wrapEl || this.el;
36708     },
36709     
36710    
36711     
36712     adjustForComponents : function(width, height)
36713     {
36714         //Roo.log('adjustForComponents ');
36715         if(this.resizeEl != this.el){
36716             width -= this.el.getFrameWidth('lr');
36717             height -= this.el.getFrameWidth('tb');
36718         }
36719         if(this.toolbar){
36720             var te = this.toolbar.getEl();
36721             te.setWidth(width);
36722             height -= te.getHeight();
36723         }
36724         if(this.footer){
36725             var te = this.footer.getEl();
36726             te.setWidth(width);
36727             height -= te.getHeight();
36728         }
36729         
36730         
36731         if(this.adjustments){
36732             width += this.adjustments[0];
36733             height += this.adjustments[1];
36734         }
36735         return {"width": width, "height": height};
36736     },
36737     
36738     setSize : function(width, height){
36739         if(this.fitToFrame && !this.ignoreResize(width, height)){
36740             if(this.fitContainer && this.resizeEl != this.el){
36741                 this.el.setSize(width, height);
36742             }
36743             var size = this.adjustForComponents(width, height);
36744             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36745             this.fireEvent('resize', this, size.width, size.height);
36746         }
36747     },
36748     
36749     /**
36750      * Returns this panel's title
36751      * @return {String} 
36752      */
36753     getTitle : function(){
36754         
36755         if (typeof(this.title) != 'object') {
36756             return this.title;
36757         }
36758         
36759         var t = '';
36760         for (var k in this.title) {
36761             if (!this.title.hasOwnProperty(k)) {
36762                 continue;
36763             }
36764             
36765             if (k.indexOf('-') >= 0) {
36766                 var s = k.split('-');
36767                 for (var i = 0; i<s.length; i++) {
36768                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36769                 }
36770             } else {
36771                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36772             }
36773         }
36774         return t;
36775     },
36776     
36777     /**
36778      * Set this panel's title
36779      * @param {String} title
36780      */
36781     setTitle : function(title){
36782         this.title = title;
36783         if(this.region){
36784             this.region.updatePanelTitle(this, title);
36785         }
36786     },
36787     
36788     /**
36789      * Returns true is this panel was configured to be closable
36790      * @return {Boolean} 
36791      */
36792     isClosable : function(){
36793         return this.closable;
36794     },
36795     
36796     beforeSlide : function(){
36797         this.el.clip();
36798         this.resizeEl.clip();
36799     },
36800     
36801     afterSlide : function(){
36802         this.el.unclip();
36803         this.resizeEl.unclip();
36804     },
36805     
36806     /**
36807      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36808      *   Will fail silently if the {@link #setUrl} method has not been called.
36809      *   This does not activate the panel, just updates its content.
36810      */
36811     refresh : function(){
36812         if(this.refreshDelegate){
36813            this.loaded = false;
36814            this.refreshDelegate();
36815         }
36816     },
36817     
36818     /**
36819      * Destroys this panel
36820      */
36821     destroy : function(){
36822         this.el.removeAllListeners();
36823         var tempEl = document.createElement("span");
36824         tempEl.appendChild(this.el.dom);
36825         tempEl.innerHTML = "";
36826         this.el.remove();
36827         this.el = null;
36828     },
36829     
36830     /**
36831      * form - if the content panel contains a form - this is a reference to it.
36832      * @type {Roo.form.Form}
36833      */
36834     form : false,
36835     /**
36836      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36837      *    This contains a reference to it.
36838      * @type {Roo.View}
36839      */
36840     view : false,
36841     
36842       /**
36843      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36844      * <pre><code>
36845
36846 layout.addxtype({
36847        xtype : 'Form',
36848        items: [ .... ]
36849    }
36850 );
36851
36852 </code></pre>
36853      * @param {Object} cfg Xtype definition of item to add.
36854      */
36855     
36856     
36857     getChildContainer: function () {
36858         return this.getEl();
36859     }
36860     
36861     
36862     /*
36863         var  ret = new Roo.factory(cfg);
36864         return ret;
36865         
36866         
36867         // add form..
36868         if (cfg.xtype.match(/^Form$/)) {
36869             
36870             var el;
36871             //if (this.footer) {
36872             //    el = this.footer.container.insertSibling(false, 'before');
36873             //} else {
36874                 el = this.el.createChild();
36875             //}
36876
36877             this.form = new  Roo.form.Form(cfg);
36878             
36879             
36880             if ( this.form.allItems.length) {
36881                 this.form.render(el.dom);
36882             }
36883             return this.form;
36884         }
36885         // should only have one of theses..
36886         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36887             // views.. should not be just added - used named prop 'view''
36888             
36889             cfg.el = this.el.appendChild(document.createElement("div"));
36890             // factory?
36891             
36892             var ret = new Roo.factory(cfg);
36893              
36894              ret.render && ret.render(false, ''); // render blank..
36895             this.view = ret;
36896             return ret;
36897         }
36898         return false;
36899     }
36900     \*/
36901 });
36902  
36903 /**
36904  * @class Roo.bootstrap.panel.Grid
36905  * @extends Roo.bootstrap.panel.Content
36906  * @constructor
36907  * Create a new GridPanel.
36908  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36909  * @param {Object} config A the config object
36910   
36911  */
36912
36913
36914
36915 Roo.bootstrap.panel.Grid = function(config)
36916 {
36917     
36918       
36919     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36920         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36921
36922     config.el = this.wrapper;
36923     //this.el = this.wrapper;
36924     
36925       if (config.container) {
36926         // ctor'ed from a Border/panel.grid
36927         
36928         
36929         this.wrapper.setStyle("overflow", "hidden");
36930         this.wrapper.addClass('roo-grid-container');
36931
36932     }
36933     
36934     
36935     if(config.toolbar){
36936         var tool_el = this.wrapper.createChild();    
36937         this.toolbar = Roo.factory(config.toolbar);
36938         var ti = [];
36939         if (config.toolbar.items) {
36940             ti = config.toolbar.items ;
36941             delete config.toolbar.items ;
36942         }
36943         
36944         var nitems = [];
36945         this.toolbar.render(tool_el);
36946         for(var i =0;i < ti.length;i++) {
36947           //  Roo.log(['add child', items[i]]);
36948             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36949         }
36950         this.toolbar.items = nitems;
36951         
36952         delete config.toolbar;
36953     }
36954     
36955     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36956     config.grid.scrollBody = true;;
36957     config.grid.monitorWindowResize = false; // turn off autosizing
36958     config.grid.autoHeight = false;
36959     config.grid.autoWidth = false;
36960     
36961     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36962     
36963     if (config.background) {
36964         // render grid on panel activation (if panel background)
36965         this.on('activate', function(gp) {
36966             if (!gp.grid.rendered) {
36967                 gp.grid.render(this.wrapper);
36968                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36969             }
36970         });
36971             
36972     } else {
36973         this.grid.render(this.wrapper);
36974         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36975
36976     }
36977     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36978     // ??? needed ??? config.el = this.wrapper;
36979     
36980     
36981     
36982   
36983     // xtype created footer. - not sure if will work as we normally have to render first..
36984     if (this.footer && !this.footer.el && this.footer.xtype) {
36985         
36986         var ctr = this.grid.getView().getFooterPanel(true);
36987         this.footer.dataSource = this.grid.dataSource;
36988         this.footer = Roo.factory(this.footer, Roo);
36989         this.footer.render(ctr);
36990         
36991     }
36992     
36993     
36994     
36995     
36996      
36997 };
36998
36999 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37000     getId : function(){
37001         return this.grid.id;
37002     },
37003     
37004     /**
37005      * Returns the grid for this panel
37006      * @return {Roo.bootstrap.Table} 
37007      */
37008     getGrid : function(){
37009         return this.grid;    
37010     },
37011     
37012     setSize : function(width, height){
37013         if(!this.ignoreResize(width, height)){
37014             var grid = this.grid;
37015             var size = this.adjustForComponents(width, height);
37016             var gridel = grid.getGridEl();
37017             gridel.setSize(size.width, size.height);
37018             /*
37019             var thd = grid.getGridEl().select('thead',true).first();
37020             var tbd = grid.getGridEl().select('tbody', true).first();
37021             if (tbd) {
37022                 tbd.setSize(width, height - thd.getHeight());
37023             }
37024             */
37025             grid.autoSize();
37026         }
37027     },
37028      
37029     
37030     
37031     beforeSlide : function(){
37032         this.grid.getView().scroller.clip();
37033     },
37034     
37035     afterSlide : function(){
37036         this.grid.getView().scroller.unclip();
37037     },
37038     
37039     destroy : function(){
37040         this.grid.destroy();
37041         delete this.grid;
37042         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37043     }
37044 });
37045
37046 /**
37047  * @class Roo.bootstrap.panel.Nest
37048  * @extends Roo.bootstrap.panel.Content
37049  * @constructor
37050  * Create a new Panel, that can contain a layout.Border.
37051  * 
37052  * 
37053  * @param {Roo.BorderLayout} layout The layout for this panel
37054  * @param {String/Object} config A string to set only the title or a config object
37055  */
37056 Roo.bootstrap.panel.Nest = function(config)
37057 {
37058     // construct with only one argument..
37059     /* FIXME - implement nicer consturctors
37060     if (layout.layout) {
37061         config = layout;
37062         layout = config.layout;
37063         delete config.layout;
37064     }
37065     if (layout.xtype && !layout.getEl) {
37066         // then layout needs constructing..
37067         layout = Roo.factory(layout, Roo);
37068     }
37069     */
37070     
37071     config.el =  config.layout.getEl();
37072     
37073     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37074     
37075     config.layout.monitorWindowResize = false; // turn off autosizing
37076     this.layout = config.layout;
37077     this.layout.getEl().addClass("roo-layout-nested-layout");
37078     
37079     
37080     
37081     
37082 };
37083
37084 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37085
37086     setSize : function(width, height){
37087         if(!this.ignoreResize(width, height)){
37088             var size = this.adjustForComponents(width, height);
37089             var el = this.layout.getEl();
37090             if (size.height < 1) {
37091                 el.setWidth(size.width);   
37092             } else {
37093                 el.setSize(size.width, size.height);
37094             }
37095             var touch = el.dom.offsetWidth;
37096             this.layout.layout();
37097             // ie requires a double layout on the first pass
37098             if(Roo.isIE && !this.initialized){
37099                 this.initialized = true;
37100                 this.layout.layout();
37101             }
37102         }
37103     },
37104     
37105     // activate all subpanels if not currently active..
37106     
37107     setActiveState : function(active){
37108         this.active = active;
37109         this.setActiveClass(active);
37110         
37111         if(!active){
37112             this.fireEvent("deactivate", this);
37113             return;
37114         }
37115         
37116         this.fireEvent("activate", this);
37117         // not sure if this should happen before or after..
37118         if (!this.layout) {
37119             return; // should not happen..
37120         }
37121         var reg = false;
37122         for (var r in this.layout.regions) {
37123             reg = this.layout.getRegion(r);
37124             if (reg.getActivePanel()) {
37125                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37126                 reg.setActivePanel(reg.getActivePanel());
37127                 continue;
37128             }
37129             if (!reg.panels.length) {
37130                 continue;
37131             }
37132             reg.showPanel(reg.getPanel(0));
37133         }
37134         
37135         
37136         
37137         
37138     },
37139     
37140     /**
37141      * Returns the nested BorderLayout for this panel
37142      * @return {Roo.BorderLayout} 
37143      */
37144     getLayout : function(){
37145         return this.layout;
37146     },
37147     
37148      /**
37149      * Adds a xtype elements to the layout of the nested panel
37150      * <pre><code>
37151
37152 panel.addxtype({
37153        xtype : 'ContentPanel',
37154        region: 'west',
37155        items: [ .... ]
37156    }
37157 );
37158
37159 panel.addxtype({
37160         xtype : 'NestedLayoutPanel',
37161         region: 'west',
37162         layout: {
37163            center: { },
37164            west: { }   
37165         },
37166         items : [ ... list of content panels or nested layout panels.. ]
37167    }
37168 );
37169 </code></pre>
37170      * @param {Object} cfg Xtype definition of item to add.
37171      */
37172     addxtype : function(cfg) {
37173         return this.layout.addxtype(cfg);
37174     
37175     }
37176 });        /*
37177  * Based on:
37178  * Ext JS Library 1.1.1
37179  * Copyright(c) 2006-2007, Ext JS, LLC.
37180  *
37181  * Originally Released Under LGPL - original licence link has changed is not relivant.
37182  *
37183  * Fork - LGPL
37184  * <script type="text/javascript">
37185  */
37186 /**
37187  * @class Roo.TabPanel
37188  * @extends Roo.util.Observable
37189  * A lightweight tab container.
37190  * <br><br>
37191  * Usage:
37192  * <pre><code>
37193 // basic tabs 1, built from existing content
37194 var tabs = new Roo.TabPanel("tabs1");
37195 tabs.addTab("script", "View Script");
37196 tabs.addTab("markup", "View Markup");
37197 tabs.activate("script");
37198
37199 // more advanced tabs, built from javascript
37200 var jtabs = new Roo.TabPanel("jtabs");
37201 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37202
37203 // set up the UpdateManager
37204 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37205 var updater = tab2.getUpdateManager();
37206 updater.setDefaultUrl("ajax1.htm");
37207 tab2.on('activate', updater.refresh, updater, true);
37208
37209 // Use setUrl for Ajax loading
37210 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37211 tab3.setUrl("ajax2.htm", null, true);
37212
37213 // Disabled tab
37214 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37215 tab4.disable();
37216
37217 jtabs.activate("jtabs-1");
37218  * </code></pre>
37219  * @constructor
37220  * Create a new TabPanel.
37221  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37222  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37223  */
37224 Roo.bootstrap.panel.Tabs = function(config){
37225     /**
37226     * The container element for this TabPanel.
37227     * @type Roo.Element
37228     */
37229     this.el = Roo.get(config.el);
37230     delete config.el;
37231     if(config){
37232         if(typeof config == "boolean"){
37233             this.tabPosition = config ? "bottom" : "top";
37234         }else{
37235             Roo.apply(this, config);
37236         }
37237     }
37238     
37239     if(this.tabPosition == "bottom"){
37240         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37241         this.el.addClass("roo-tabs-bottom");
37242     }
37243     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37244     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37245     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37246     if(Roo.isIE){
37247         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37248     }
37249     if(this.tabPosition != "bottom"){
37250         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37251          * @type Roo.Element
37252          */
37253         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37254         this.el.addClass("roo-tabs-top");
37255     }
37256     this.items = [];
37257
37258     this.bodyEl.setStyle("position", "relative");
37259
37260     this.active = null;
37261     this.activateDelegate = this.activate.createDelegate(this);
37262
37263     this.addEvents({
37264         /**
37265          * @event tabchange
37266          * Fires when the active tab changes
37267          * @param {Roo.TabPanel} this
37268          * @param {Roo.TabPanelItem} activePanel The new active tab
37269          */
37270         "tabchange": true,
37271         /**
37272          * @event beforetabchange
37273          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37274          * @param {Roo.TabPanel} this
37275          * @param {Object} e Set cancel to true on this object to cancel the tab change
37276          * @param {Roo.TabPanelItem} tab The tab being changed to
37277          */
37278         "beforetabchange" : true
37279     });
37280
37281     Roo.EventManager.onWindowResize(this.onResize, this);
37282     this.cpad = this.el.getPadding("lr");
37283     this.hiddenCount = 0;
37284
37285
37286     // toolbar on the tabbar support...
37287     if (this.toolbar) {
37288         alert("no toolbar support yet");
37289         this.toolbar  = false;
37290         /*
37291         var tcfg = this.toolbar;
37292         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37293         this.toolbar = new Roo.Toolbar(tcfg);
37294         if (Roo.isSafari) {
37295             var tbl = tcfg.container.child('table', true);
37296             tbl.setAttribute('width', '100%');
37297         }
37298         */
37299         
37300     }
37301    
37302
37303
37304     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37305 };
37306
37307 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37308     /*
37309      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37310      */
37311     tabPosition : "top",
37312     /*
37313      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37314      */
37315     currentTabWidth : 0,
37316     /*
37317      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37318      */
37319     minTabWidth : 40,
37320     /*
37321      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37322      */
37323     maxTabWidth : 250,
37324     /*
37325      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37326      */
37327     preferredTabWidth : 175,
37328     /*
37329      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37330      */
37331     resizeTabs : false,
37332     /*
37333      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37334      */
37335     monitorResize : true,
37336     /*
37337      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37338      */
37339     toolbar : false,
37340
37341     /**
37342      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37343      * @param {String} id The id of the div to use <b>or create</b>
37344      * @param {String} text The text for the tab
37345      * @param {String} content (optional) Content to put in the TabPanelItem body
37346      * @param {Boolean} closable (optional) True to create a close icon on the tab
37347      * @return {Roo.TabPanelItem} The created TabPanelItem
37348      */
37349     addTab : function(id, text, content, closable, tpl)
37350     {
37351         var item = new Roo.bootstrap.panel.TabItem({
37352             panel: this,
37353             id : id,
37354             text : text,
37355             closable : closable,
37356             tpl : tpl
37357         });
37358         this.addTabItem(item);
37359         if(content){
37360             item.setContent(content);
37361         }
37362         return item;
37363     },
37364
37365     /**
37366      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37367      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37368      * @return {Roo.TabPanelItem}
37369      */
37370     getTab : function(id){
37371         return this.items[id];
37372     },
37373
37374     /**
37375      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37376      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37377      */
37378     hideTab : function(id){
37379         var t = this.items[id];
37380         if(!t.isHidden()){
37381            t.setHidden(true);
37382            this.hiddenCount++;
37383            this.autoSizeTabs();
37384         }
37385     },
37386
37387     /**
37388      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37389      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37390      */
37391     unhideTab : function(id){
37392         var t = this.items[id];
37393         if(t.isHidden()){
37394            t.setHidden(false);
37395            this.hiddenCount--;
37396            this.autoSizeTabs();
37397         }
37398     },
37399
37400     /**
37401      * Adds an existing {@link Roo.TabPanelItem}.
37402      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37403      */
37404     addTabItem : function(item){
37405         this.items[item.id] = item;
37406         this.items.push(item);
37407       //  if(this.resizeTabs){
37408     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37409   //         this.autoSizeTabs();
37410 //        }else{
37411 //            item.autoSize();
37412        // }
37413     },
37414
37415     /**
37416      * Removes a {@link Roo.TabPanelItem}.
37417      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37418      */
37419     removeTab : function(id){
37420         var items = this.items;
37421         var tab = items[id];
37422         if(!tab) { return; }
37423         var index = items.indexOf(tab);
37424         if(this.active == tab && items.length > 1){
37425             var newTab = this.getNextAvailable(index);
37426             if(newTab) {
37427                 newTab.activate();
37428             }
37429         }
37430         this.stripEl.dom.removeChild(tab.pnode.dom);
37431         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37432             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37433         }
37434         items.splice(index, 1);
37435         delete this.items[tab.id];
37436         tab.fireEvent("close", tab);
37437         tab.purgeListeners();
37438         this.autoSizeTabs();
37439     },
37440
37441     getNextAvailable : function(start){
37442         var items = this.items;
37443         var index = start;
37444         // look for a next tab that will slide over to
37445         // replace the one being removed
37446         while(index < items.length){
37447             var item = items[++index];
37448             if(item && !item.isHidden()){
37449                 return item;
37450             }
37451         }
37452         // if one isn't found select the previous tab (on the left)
37453         index = start;
37454         while(index >= 0){
37455             var item = items[--index];
37456             if(item && !item.isHidden()){
37457                 return item;
37458             }
37459         }
37460         return null;
37461     },
37462
37463     /**
37464      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37465      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37466      */
37467     disableTab : function(id){
37468         var tab = this.items[id];
37469         if(tab && this.active != tab){
37470             tab.disable();
37471         }
37472     },
37473
37474     /**
37475      * Enables a {@link Roo.TabPanelItem} that is disabled.
37476      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37477      */
37478     enableTab : function(id){
37479         var tab = this.items[id];
37480         tab.enable();
37481     },
37482
37483     /**
37484      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37485      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37486      * @return {Roo.TabPanelItem} The TabPanelItem.
37487      */
37488     activate : function(id){
37489         var tab = this.items[id];
37490         if(!tab){
37491             return null;
37492         }
37493         if(tab == this.active || tab.disabled){
37494             return tab;
37495         }
37496         var e = {};
37497         this.fireEvent("beforetabchange", this, e, tab);
37498         if(e.cancel !== true && !tab.disabled){
37499             if(this.active){
37500                 this.active.hide();
37501             }
37502             this.active = this.items[id];
37503             this.active.show();
37504             this.fireEvent("tabchange", this, this.active);
37505         }
37506         return tab;
37507     },
37508
37509     /**
37510      * Gets the active {@link Roo.TabPanelItem}.
37511      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37512      */
37513     getActiveTab : function(){
37514         return this.active;
37515     },
37516
37517     /**
37518      * Updates the tab body element to fit the height of the container element
37519      * for overflow scrolling
37520      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37521      */
37522     syncHeight : function(targetHeight){
37523         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37524         var bm = this.bodyEl.getMargins();
37525         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37526         this.bodyEl.setHeight(newHeight);
37527         return newHeight;
37528     },
37529
37530     onResize : function(){
37531         if(this.monitorResize){
37532             this.autoSizeTabs();
37533         }
37534     },
37535
37536     /**
37537      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37538      */
37539     beginUpdate : function(){
37540         this.updating = true;
37541     },
37542
37543     /**
37544      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37545      */
37546     endUpdate : function(){
37547         this.updating = false;
37548         this.autoSizeTabs();
37549     },
37550
37551     /**
37552      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37553      */
37554     autoSizeTabs : function(){
37555         var count = this.items.length;
37556         var vcount = count - this.hiddenCount;
37557         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37558             return;
37559         }
37560         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37561         var availWidth = Math.floor(w / vcount);
37562         var b = this.stripBody;
37563         if(b.getWidth() > w){
37564             var tabs = this.items;
37565             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37566             if(availWidth < this.minTabWidth){
37567                 /*if(!this.sleft){    // incomplete scrolling code
37568                     this.createScrollButtons();
37569                 }
37570                 this.showScroll();
37571                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37572             }
37573         }else{
37574             if(this.currentTabWidth < this.preferredTabWidth){
37575                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37576             }
37577         }
37578     },
37579
37580     /**
37581      * Returns the number of tabs in this TabPanel.
37582      * @return {Number}
37583      */
37584      getCount : function(){
37585          return this.items.length;
37586      },
37587
37588     /**
37589      * Resizes all the tabs to the passed width
37590      * @param {Number} The new width
37591      */
37592     setTabWidth : function(width){
37593         this.currentTabWidth = width;
37594         for(var i = 0, len = this.items.length; i < len; i++) {
37595                 if(!this.items[i].isHidden()) {
37596                 this.items[i].setWidth(width);
37597             }
37598         }
37599     },
37600
37601     /**
37602      * Destroys this TabPanel
37603      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37604      */
37605     destroy : function(removeEl){
37606         Roo.EventManager.removeResizeListener(this.onResize, this);
37607         for(var i = 0, len = this.items.length; i < len; i++){
37608             this.items[i].purgeListeners();
37609         }
37610         if(removeEl === true){
37611             this.el.update("");
37612             this.el.remove();
37613         }
37614     },
37615     
37616     createStrip : function(container)
37617     {
37618         var strip = document.createElement("nav");
37619         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37620         container.appendChild(strip);
37621         return strip;
37622     },
37623     
37624     createStripList : function(strip)
37625     {
37626         // div wrapper for retard IE
37627         // returns the "tr" element.
37628         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37629         //'<div class="x-tabs-strip-wrap">'+
37630           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37631           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37632         return strip.firstChild; //.firstChild.firstChild.firstChild;
37633     },
37634     createBody : function(container)
37635     {
37636         var body = document.createElement("div");
37637         Roo.id(body, "tab-body");
37638         //Roo.fly(body).addClass("x-tabs-body");
37639         Roo.fly(body).addClass("tab-content");
37640         container.appendChild(body);
37641         return body;
37642     },
37643     createItemBody :function(bodyEl, id){
37644         var body = Roo.getDom(id);
37645         if(!body){
37646             body = document.createElement("div");
37647             body.id = id;
37648         }
37649         //Roo.fly(body).addClass("x-tabs-item-body");
37650         Roo.fly(body).addClass("tab-pane");
37651          bodyEl.insertBefore(body, bodyEl.firstChild);
37652         return body;
37653     },
37654     /** @private */
37655     createStripElements :  function(stripEl, text, closable, tpl)
37656     {
37657         var td = document.createElement("li"); // was td..
37658         
37659         
37660         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37661         
37662         
37663         stripEl.appendChild(td);
37664         /*if(closable){
37665             td.className = "x-tabs-closable";
37666             if(!this.closeTpl){
37667                 this.closeTpl = new Roo.Template(
37668                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37669                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37670                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37671                 );
37672             }
37673             var el = this.closeTpl.overwrite(td, {"text": text});
37674             var close = el.getElementsByTagName("div")[0];
37675             var inner = el.getElementsByTagName("em")[0];
37676             return {"el": el, "close": close, "inner": inner};
37677         } else {
37678         */
37679         // not sure what this is..
37680 //            if(!this.tabTpl){
37681                 //this.tabTpl = new Roo.Template(
37682                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37683                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37684                 //);
37685 //                this.tabTpl = new Roo.Template(
37686 //                   '<a href="#">' +
37687 //                   '<span unselectable="on"' +
37688 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37689 //                            ' >{text}</span></a>'
37690 //                );
37691 //                
37692 //            }
37693
37694
37695             var template = tpl || this.tabTpl || false;
37696             
37697             if(!template){
37698                 
37699                 template = new Roo.Template(
37700                    '<a href="#">' +
37701                    '<span unselectable="on"' +
37702                             (this.disableTooltips ? '' : ' title="{text}"') +
37703                             ' >{text}</span></a>'
37704                 );
37705             }
37706             
37707             switch (typeof(template)) {
37708                 case 'object' :
37709                     break;
37710                 case 'string' :
37711                     template = new Roo.Template(template);
37712                     break;
37713                 default :
37714                     break;
37715             }
37716             
37717             var el = template.overwrite(td, {"text": text});
37718             
37719             var inner = el.getElementsByTagName("span")[0];
37720             
37721             return {"el": el, "inner": inner};
37722             
37723     }
37724         
37725     
37726 });
37727
37728 /**
37729  * @class Roo.TabPanelItem
37730  * @extends Roo.util.Observable
37731  * Represents an individual item (tab plus body) in a TabPanel.
37732  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37733  * @param {String} id The id of this TabPanelItem
37734  * @param {String} text The text for the tab of this TabPanelItem
37735  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37736  */
37737 Roo.bootstrap.panel.TabItem = function(config){
37738     /**
37739      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37740      * @type Roo.TabPanel
37741      */
37742     this.tabPanel = config.panel;
37743     /**
37744      * The id for this TabPanelItem
37745      * @type String
37746      */
37747     this.id = config.id;
37748     /** @private */
37749     this.disabled = false;
37750     /** @private */
37751     this.text = config.text;
37752     /** @private */
37753     this.loaded = false;
37754     this.closable = config.closable;
37755
37756     /**
37757      * The body element for this TabPanelItem.
37758      * @type Roo.Element
37759      */
37760     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37761     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37762     this.bodyEl.setStyle("display", "block");
37763     this.bodyEl.setStyle("zoom", "1");
37764     //this.hideAction();
37765
37766     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37767     /** @private */
37768     this.el = Roo.get(els.el);
37769     this.inner = Roo.get(els.inner, true);
37770     this.textEl = Roo.get(this.el.dom.firstChild, true);
37771     this.pnode = Roo.get(els.el.parentNode, true);
37772 //    this.el.on("mousedown", this.onTabMouseDown, this);
37773     this.el.on("click", this.onTabClick, this);
37774     /** @private */
37775     if(config.closable){
37776         var c = Roo.get(els.close, true);
37777         c.dom.title = this.closeText;
37778         c.addClassOnOver("close-over");
37779         c.on("click", this.closeClick, this);
37780      }
37781
37782     this.addEvents({
37783          /**
37784          * @event activate
37785          * Fires when this tab becomes the active tab.
37786          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37787          * @param {Roo.TabPanelItem} this
37788          */
37789         "activate": true,
37790         /**
37791          * @event beforeclose
37792          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37793          * @param {Roo.TabPanelItem} this
37794          * @param {Object} e Set cancel to true on this object to cancel the close.
37795          */
37796         "beforeclose": true,
37797         /**
37798          * @event close
37799          * Fires when this tab is closed.
37800          * @param {Roo.TabPanelItem} this
37801          */
37802          "close": true,
37803         /**
37804          * @event deactivate
37805          * Fires when this tab is no longer the active tab.
37806          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37807          * @param {Roo.TabPanelItem} this
37808          */
37809          "deactivate" : true
37810     });
37811     this.hidden = false;
37812
37813     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37814 };
37815
37816 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37817            {
37818     purgeListeners : function(){
37819        Roo.util.Observable.prototype.purgeListeners.call(this);
37820        this.el.removeAllListeners();
37821     },
37822     /**
37823      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37824      */
37825     show : function(){
37826         this.pnode.addClass("active");
37827         this.showAction();
37828         if(Roo.isOpera){
37829             this.tabPanel.stripWrap.repaint();
37830         }
37831         this.fireEvent("activate", this.tabPanel, this);
37832     },
37833
37834     /**
37835      * Returns true if this tab is the active tab.
37836      * @return {Boolean}
37837      */
37838     isActive : function(){
37839         return this.tabPanel.getActiveTab() == this;
37840     },
37841
37842     /**
37843      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37844      */
37845     hide : function(){
37846         this.pnode.removeClass("active");
37847         this.hideAction();
37848         this.fireEvent("deactivate", this.tabPanel, this);
37849     },
37850
37851     hideAction : function(){
37852         this.bodyEl.hide();
37853         this.bodyEl.setStyle("position", "absolute");
37854         this.bodyEl.setLeft("-20000px");
37855         this.bodyEl.setTop("-20000px");
37856     },
37857
37858     showAction : function(){
37859         this.bodyEl.setStyle("position", "relative");
37860         this.bodyEl.setTop("");
37861         this.bodyEl.setLeft("");
37862         this.bodyEl.show();
37863     },
37864
37865     /**
37866      * Set the tooltip for the tab.
37867      * @param {String} tooltip The tab's tooltip
37868      */
37869     setTooltip : function(text){
37870         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37871             this.textEl.dom.qtip = text;
37872             this.textEl.dom.removeAttribute('title');
37873         }else{
37874             this.textEl.dom.title = text;
37875         }
37876     },
37877
37878     onTabClick : function(e){
37879         e.preventDefault();
37880         this.tabPanel.activate(this.id);
37881     },
37882
37883     onTabMouseDown : function(e){
37884         e.preventDefault();
37885         this.tabPanel.activate(this.id);
37886     },
37887 /*
37888     getWidth : function(){
37889         return this.inner.getWidth();
37890     },
37891
37892     setWidth : function(width){
37893         var iwidth = width - this.pnode.getPadding("lr");
37894         this.inner.setWidth(iwidth);
37895         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37896         this.pnode.setWidth(width);
37897     },
37898 */
37899     /**
37900      * Show or hide the tab
37901      * @param {Boolean} hidden True to hide or false to show.
37902      */
37903     setHidden : function(hidden){
37904         this.hidden = hidden;
37905         this.pnode.setStyle("display", hidden ? "none" : "");
37906     },
37907
37908     /**
37909      * Returns true if this tab is "hidden"
37910      * @return {Boolean}
37911      */
37912     isHidden : function(){
37913         return this.hidden;
37914     },
37915
37916     /**
37917      * Returns the text for this tab
37918      * @return {String}
37919      */
37920     getText : function(){
37921         return this.text;
37922     },
37923     /*
37924     autoSize : function(){
37925         //this.el.beginMeasure();
37926         this.textEl.setWidth(1);
37927         /*
37928          *  #2804 [new] Tabs in Roojs
37929          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37930          */
37931         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37932         //this.el.endMeasure();
37933     //},
37934
37935     /**
37936      * Sets the text for the tab (Note: this also sets the tooltip text)
37937      * @param {String} text The tab's text and tooltip
37938      */
37939     setText : function(text){
37940         this.text = text;
37941         this.textEl.update(text);
37942         this.setTooltip(text);
37943         //if(!this.tabPanel.resizeTabs){
37944         //    this.autoSize();
37945         //}
37946     },
37947     /**
37948      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37949      */
37950     activate : function(){
37951         this.tabPanel.activate(this.id);
37952     },
37953
37954     /**
37955      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37956      */
37957     disable : function(){
37958         if(this.tabPanel.active != this){
37959             this.disabled = true;
37960             this.pnode.addClass("disabled");
37961         }
37962     },
37963
37964     /**
37965      * Enables this TabPanelItem if it was previously disabled.
37966      */
37967     enable : function(){
37968         this.disabled = false;
37969         this.pnode.removeClass("disabled");
37970     },
37971
37972     /**
37973      * Sets the content for this TabPanelItem.
37974      * @param {String} content The content
37975      * @param {Boolean} loadScripts true to look for and load scripts
37976      */
37977     setContent : function(content, loadScripts){
37978         this.bodyEl.update(content, loadScripts);
37979     },
37980
37981     /**
37982      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37983      * @return {Roo.UpdateManager} The UpdateManager
37984      */
37985     getUpdateManager : function(){
37986         return this.bodyEl.getUpdateManager();
37987     },
37988
37989     /**
37990      * Set a URL to be used to load the content for this TabPanelItem.
37991      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37992      * @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)
37993      * @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)
37994      * @return {Roo.UpdateManager} The UpdateManager
37995      */
37996     setUrl : function(url, params, loadOnce){
37997         if(this.refreshDelegate){
37998             this.un('activate', this.refreshDelegate);
37999         }
38000         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38001         this.on("activate", this.refreshDelegate);
38002         return this.bodyEl.getUpdateManager();
38003     },
38004
38005     /** @private */
38006     _handleRefresh : function(url, params, loadOnce){
38007         if(!loadOnce || !this.loaded){
38008             var updater = this.bodyEl.getUpdateManager();
38009             updater.update(url, params, this._setLoaded.createDelegate(this));
38010         }
38011     },
38012
38013     /**
38014      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38015      *   Will fail silently if the setUrl method has not been called.
38016      *   This does not activate the panel, just updates its content.
38017      */
38018     refresh : function(){
38019         if(this.refreshDelegate){
38020            this.loaded = false;
38021            this.refreshDelegate();
38022         }
38023     },
38024
38025     /** @private */
38026     _setLoaded : function(){
38027         this.loaded = true;
38028     },
38029
38030     /** @private */
38031     closeClick : function(e){
38032         var o = {};
38033         e.stopEvent();
38034         this.fireEvent("beforeclose", this, o);
38035         if(o.cancel !== true){
38036             this.tabPanel.removeTab(this.id);
38037         }
38038     },
38039     /**
38040      * The text displayed in the tooltip for the close icon.
38041      * @type String
38042      */
38043     closeText : "Close this tab"
38044 });
38045 /**
38046 *    This script refer to:
38047 *    Title: International Telephone Input
38048 *    Author: Jack O'Connor
38049 *    Code version:  v12.1.12
38050 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38051 **/
38052
38053 Roo.bootstrap.PhoneInputData = function() {
38054     var d = [
38055       [
38056         "Afghanistan (‫افغانستان‬‎)",
38057         "af",
38058         "93"
38059       ],
38060       [
38061         "Albania (Shqipëri)",
38062         "al",
38063         "355"
38064       ],
38065       [
38066         "Algeria (‫الجزائر‬‎)",
38067         "dz",
38068         "213"
38069       ],
38070       [
38071         "American Samoa",
38072         "as",
38073         "1684"
38074       ],
38075       [
38076         "Andorra",
38077         "ad",
38078         "376"
38079       ],
38080       [
38081         "Angola",
38082         "ao",
38083         "244"
38084       ],
38085       [
38086         "Anguilla",
38087         "ai",
38088         "1264"
38089       ],
38090       [
38091         "Antigua and Barbuda",
38092         "ag",
38093         "1268"
38094       ],
38095       [
38096         "Argentina",
38097         "ar",
38098         "54"
38099       ],
38100       [
38101         "Armenia (Հայաստան)",
38102         "am",
38103         "374"
38104       ],
38105       [
38106         "Aruba",
38107         "aw",
38108         "297"
38109       ],
38110       [
38111         "Australia",
38112         "au",
38113         "61",
38114         0
38115       ],
38116       [
38117         "Austria (Österreich)",
38118         "at",
38119         "43"
38120       ],
38121       [
38122         "Azerbaijan (Azərbaycan)",
38123         "az",
38124         "994"
38125       ],
38126       [
38127         "Bahamas",
38128         "bs",
38129         "1242"
38130       ],
38131       [
38132         "Bahrain (‫البحرين‬‎)",
38133         "bh",
38134         "973"
38135       ],
38136       [
38137         "Bangladesh (বাংলাদেশ)",
38138         "bd",
38139         "880"
38140       ],
38141       [
38142         "Barbados",
38143         "bb",
38144         "1246"
38145       ],
38146       [
38147         "Belarus (Беларусь)",
38148         "by",
38149         "375"
38150       ],
38151       [
38152         "Belgium (België)",
38153         "be",
38154         "32"
38155       ],
38156       [
38157         "Belize",
38158         "bz",
38159         "501"
38160       ],
38161       [
38162         "Benin (Bénin)",
38163         "bj",
38164         "229"
38165       ],
38166       [
38167         "Bermuda",
38168         "bm",
38169         "1441"
38170       ],
38171       [
38172         "Bhutan (འབྲུག)",
38173         "bt",
38174         "975"
38175       ],
38176       [
38177         "Bolivia",
38178         "bo",
38179         "591"
38180       ],
38181       [
38182         "Bosnia and Herzegovina (Босна и Херцеговина)",
38183         "ba",
38184         "387"
38185       ],
38186       [
38187         "Botswana",
38188         "bw",
38189         "267"
38190       ],
38191       [
38192         "Brazil (Brasil)",
38193         "br",
38194         "55"
38195       ],
38196       [
38197         "British Indian Ocean Territory",
38198         "io",
38199         "246"
38200       ],
38201       [
38202         "British Virgin Islands",
38203         "vg",
38204         "1284"
38205       ],
38206       [
38207         "Brunei",
38208         "bn",
38209         "673"
38210       ],
38211       [
38212         "Bulgaria (България)",
38213         "bg",
38214         "359"
38215       ],
38216       [
38217         "Burkina Faso",
38218         "bf",
38219         "226"
38220       ],
38221       [
38222         "Burundi (Uburundi)",
38223         "bi",
38224         "257"
38225       ],
38226       [
38227         "Cambodia (កម្ពុជា)",
38228         "kh",
38229         "855"
38230       ],
38231       [
38232         "Cameroon (Cameroun)",
38233         "cm",
38234         "237"
38235       ],
38236       [
38237         "Canada",
38238         "ca",
38239         "1",
38240         1,
38241         ["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"]
38242       ],
38243       [
38244         "Cape Verde (Kabu Verdi)",
38245         "cv",
38246         "238"
38247       ],
38248       [
38249         "Caribbean Netherlands",
38250         "bq",
38251         "599",
38252         1
38253       ],
38254       [
38255         "Cayman Islands",
38256         "ky",
38257         "1345"
38258       ],
38259       [
38260         "Central African Republic (République centrafricaine)",
38261         "cf",
38262         "236"
38263       ],
38264       [
38265         "Chad (Tchad)",
38266         "td",
38267         "235"
38268       ],
38269       [
38270         "Chile",
38271         "cl",
38272         "56"
38273       ],
38274       [
38275         "China (中国)",
38276         "cn",
38277         "86"
38278       ],
38279       [
38280         "Christmas Island",
38281         "cx",
38282         "61",
38283         2
38284       ],
38285       [
38286         "Cocos (Keeling) Islands",
38287         "cc",
38288         "61",
38289         1
38290       ],
38291       [
38292         "Colombia",
38293         "co",
38294         "57"
38295       ],
38296       [
38297         "Comoros (‫جزر القمر‬‎)",
38298         "km",
38299         "269"
38300       ],
38301       [
38302         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38303         "cd",
38304         "243"
38305       ],
38306       [
38307         "Congo (Republic) (Congo-Brazzaville)",
38308         "cg",
38309         "242"
38310       ],
38311       [
38312         "Cook Islands",
38313         "ck",
38314         "682"
38315       ],
38316       [
38317         "Costa Rica",
38318         "cr",
38319         "506"
38320       ],
38321       [
38322         "Côte d’Ivoire",
38323         "ci",
38324         "225"
38325       ],
38326       [
38327         "Croatia (Hrvatska)",
38328         "hr",
38329         "385"
38330       ],
38331       [
38332         "Cuba",
38333         "cu",
38334         "53"
38335       ],
38336       [
38337         "Curaçao",
38338         "cw",
38339         "599",
38340         0
38341       ],
38342       [
38343         "Cyprus (Κύπρος)",
38344         "cy",
38345         "357"
38346       ],
38347       [
38348         "Czech Republic (Česká republika)",
38349         "cz",
38350         "420"
38351       ],
38352       [
38353         "Denmark (Danmark)",
38354         "dk",
38355         "45"
38356       ],
38357       [
38358         "Djibouti",
38359         "dj",
38360         "253"
38361       ],
38362       [
38363         "Dominica",
38364         "dm",
38365         "1767"
38366       ],
38367       [
38368         "Dominican Republic (República Dominicana)",
38369         "do",
38370         "1",
38371         2,
38372         ["809", "829", "849"]
38373       ],
38374       [
38375         "Ecuador",
38376         "ec",
38377         "593"
38378       ],
38379       [
38380         "Egypt (‫مصر‬‎)",
38381         "eg",
38382         "20"
38383       ],
38384       [
38385         "El Salvador",
38386         "sv",
38387         "503"
38388       ],
38389       [
38390         "Equatorial Guinea (Guinea Ecuatorial)",
38391         "gq",
38392         "240"
38393       ],
38394       [
38395         "Eritrea",
38396         "er",
38397         "291"
38398       ],
38399       [
38400         "Estonia (Eesti)",
38401         "ee",
38402         "372"
38403       ],
38404       [
38405         "Ethiopia",
38406         "et",
38407         "251"
38408       ],
38409       [
38410         "Falkland Islands (Islas Malvinas)",
38411         "fk",
38412         "500"
38413       ],
38414       [
38415         "Faroe Islands (Føroyar)",
38416         "fo",
38417         "298"
38418       ],
38419       [
38420         "Fiji",
38421         "fj",
38422         "679"
38423       ],
38424       [
38425         "Finland (Suomi)",
38426         "fi",
38427         "358",
38428         0
38429       ],
38430       [
38431         "France",
38432         "fr",
38433         "33"
38434       ],
38435       [
38436         "French Guiana (Guyane française)",
38437         "gf",
38438         "594"
38439       ],
38440       [
38441         "French Polynesia (Polynésie française)",
38442         "pf",
38443         "689"
38444       ],
38445       [
38446         "Gabon",
38447         "ga",
38448         "241"
38449       ],
38450       [
38451         "Gambia",
38452         "gm",
38453         "220"
38454       ],
38455       [
38456         "Georgia (საქართველო)",
38457         "ge",
38458         "995"
38459       ],
38460       [
38461         "Germany (Deutschland)",
38462         "de",
38463         "49"
38464       ],
38465       [
38466         "Ghana (Gaana)",
38467         "gh",
38468         "233"
38469       ],
38470       [
38471         "Gibraltar",
38472         "gi",
38473         "350"
38474       ],
38475       [
38476         "Greece (Ελλάδα)",
38477         "gr",
38478         "30"
38479       ],
38480       [
38481         "Greenland (Kalaallit Nunaat)",
38482         "gl",
38483         "299"
38484       ],
38485       [
38486         "Grenada",
38487         "gd",
38488         "1473"
38489       ],
38490       [
38491         "Guadeloupe",
38492         "gp",
38493         "590",
38494         0
38495       ],
38496       [
38497         "Guam",
38498         "gu",
38499         "1671"
38500       ],
38501       [
38502         "Guatemala",
38503         "gt",
38504         "502"
38505       ],
38506       [
38507         "Guernsey",
38508         "gg",
38509         "44",
38510         1
38511       ],
38512       [
38513         "Guinea (Guinée)",
38514         "gn",
38515         "224"
38516       ],
38517       [
38518         "Guinea-Bissau (Guiné Bissau)",
38519         "gw",
38520         "245"
38521       ],
38522       [
38523         "Guyana",
38524         "gy",
38525         "592"
38526       ],
38527       [
38528         "Haiti",
38529         "ht",
38530         "509"
38531       ],
38532       [
38533         "Honduras",
38534         "hn",
38535         "504"
38536       ],
38537       [
38538         "Hong Kong (香港)",
38539         "hk",
38540         "852"
38541       ],
38542       [
38543         "Hungary (Magyarország)",
38544         "hu",
38545         "36"
38546       ],
38547       [
38548         "Iceland (Ísland)",
38549         "is",
38550         "354"
38551       ],
38552       [
38553         "India (भारत)",
38554         "in",
38555         "91"
38556       ],
38557       [
38558         "Indonesia",
38559         "id",
38560         "62"
38561       ],
38562       [
38563         "Iran (‫ایران‬‎)",
38564         "ir",
38565         "98"
38566       ],
38567       [
38568         "Iraq (‫العراق‬‎)",
38569         "iq",
38570         "964"
38571       ],
38572       [
38573         "Ireland",
38574         "ie",
38575         "353"
38576       ],
38577       [
38578         "Isle of Man",
38579         "im",
38580         "44",
38581         2
38582       ],
38583       [
38584         "Israel (‫ישראל‬‎)",
38585         "il",
38586         "972"
38587       ],
38588       [
38589         "Italy (Italia)",
38590         "it",
38591         "39",
38592         0
38593       ],
38594       [
38595         "Jamaica",
38596         "jm",
38597         "1876"
38598       ],
38599       [
38600         "Japan (日本)",
38601         "jp",
38602         "81"
38603       ],
38604       [
38605         "Jersey",
38606         "je",
38607         "44",
38608         3
38609       ],
38610       [
38611         "Jordan (‫الأردن‬‎)",
38612         "jo",
38613         "962"
38614       ],
38615       [
38616         "Kazakhstan (Казахстан)",
38617         "kz",
38618         "7",
38619         1
38620       ],
38621       [
38622         "Kenya",
38623         "ke",
38624         "254"
38625       ],
38626       [
38627         "Kiribati",
38628         "ki",
38629         "686"
38630       ],
38631       [
38632         "Kosovo",
38633         "xk",
38634         "383"
38635       ],
38636       [
38637         "Kuwait (‫الكويت‬‎)",
38638         "kw",
38639         "965"
38640       ],
38641       [
38642         "Kyrgyzstan (Кыргызстан)",
38643         "kg",
38644         "996"
38645       ],
38646       [
38647         "Laos (ລາວ)",
38648         "la",
38649         "856"
38650       ],
38651       [
38652         "Latvia (Latvija)",
38653         "lv",
38654         "371"
38655       ],
38656       [
38657         "Lebanon (‫لبنان‬‎)",
38658         "lb",
38659         "961"
38660       ],
38661       [
38662         "Lesotho",
38663         "ls",
38664         "266"
38665       ],
38666       [
38667         "Liberia",
38668         "lr",
38669         "231"
38670       ],
38671       [
38672         "Libya (‫ليبيا‬‎)",
38673         "ly",
38674         "218"
38675       ],
38676       [
38677         "Liechtenstein",
38678         "li",
38679         "423"
38680       ],
38681       [
38682         "Lithuania (Lietuva)",
38683         "lt",
38684         "370"
38685       ],
38686       [
38687         "Luxembourg",
38688         "lu",
38689         "352"
38690       ],
38691       [
38692         "Macau (澳門)",
38693         "mo",
38694         "853"
38695       ],
38696       [
38697         "Macedonia (FYROM) (Македонија)",
38698         "mk",
38699         "389"
38700       ],
38701       [
38702         "Madagascar (Madagasikara)",
38703         "mg",
38704         "261"
38705       ],
38706       [
38707         "Malawi",
38708         "mw",
38709         "265"
38710       ],
38711       [
38712         "Malaysia",
38713         "my",
38714         "60"
38715       ],
38716       [
38717         "Maldives",
38718         "mv",
38719         "960"
38720       ],
38721       [
38722         "Mali",
38723         "ml",
38724         "223"
38725       ],
38726       [
38727         "Malta",
38728         "mt",
38729         "356"
38730       ],
38731       [
38732         "Marshall Islands",
38733         "mh",
38734         "692"
38735       ],
38736       [
38737         "Martinique",
38738         "mq",
38739         "596"
38740       ],
38741       [
38742         "Mauritania (‫موريتانيا‬‎)",
38743         "mr",
38744         "222"
38745       ],
38746       [
38747         "Mauritius (Moris)",
38748         "mu",
38749         "230"
38750       ],
38751       [
38752         "Mayotte",
38753         "yt",
38754         "262",
38755         1
38756       ],
38757       [
38758         "Mexico (México)",
38759         "mx",
38760         "52"
38761       ],
38762       [
38763         "Micronesia",
38764         "fm",
38765         "691"
38766       ],
38767       [
38768         "Moldova (Republica Moldova)",
38769         "md",
38770         "373"
38771       ],
38772       [
38773         "Monaco",
38774         "mc",
38775         "377"
38776       ],
38777       [
38778         "Mongolia (Монгол)",
38779         "mn",
38780         "976"
38781       ],
38782       [
38783         "Montenegro (Crna Gora)",
38784         "me",
38785         "382"
38786       ],
38787       [
38788         "Montserrat",
38789         "ms",
38790         "1664"
38791       ],
38792       [
38793         "Morocco (‫المغرب‬‎)",
38794         "ma",
38795         "212",
38796         0
38797       ],
38798       [
38799         "Mozambique (Moçambique)",
38800         "mz",
38801         "258"
38802       ],
38803       [
38804         "Myanmar (Burma) (မြန်မာ)",
38805         "mm",
38806         "95"
38807       ],
38808       [
38809         "Namibia (Namibië)",
38810         "na",
38811         "264"
38812       ],
38813       [
38814         "Nauru",
38815         "nr",
38816         "674"
38817       ],
38818       [
38819         "Nepal (नेपाल)",
38820         "np",
38821         "977"
38822       ],
38823       [
38824         "Netherlands (Nederland)",
38825         "nl",
38826         "31"
38827       ],
38828       [
38829         "New Caledonia (Nouvelle-Calédonie)",
38830         "nc",
38831         "687"
38832       ],
38833       [
38834         "New Zealand",
38835         "nz",
38836         "64"
38837       ],
38838       [
38839         "Nicaragua",
38840         "ni",
38841         "505"
38842       ],
38843       [
38844         "Niger (Nijar)",
38845         "ne",
38846         "227"
38847       ],
38848       [
38849         "Nigeria",
38850         "ng",
38851         "234"
38852       ],
38853       [
38854         "Niue",
38855         "nu",
38856         "683"
38857       ],
38858       [
38859         "Norfolk Island",
38860         "nf",
38861         "672"
38862       ],
38863       [
38864         "North Korea (조선 민주주의 인민 공화국)",
38865         "kp",
38866         "850"
38867       ],
38868       [
38869         "Northern Mariana Islands",
38870         "mp",
38871         "1670"
38872       ],
38873       [
38874         "Norway (Norge)",
38875         "no",
38876         "47",
38877         0
38878       ],
38879       [
38880         "Oman (‫عُمان‬‎)",
38881         "om",
38882         "968"
38883       ],
38884       [
38885         "Pakistan (‫پاکستان‬‎)",
38886         "pk",
38887         "92"
38888       ],
38889       [
38890         "Palau",
38891         "pw",
38892         "680"
38893       ],
38894       [
38895         "Palestine (‫فلسطين‬‎)",
38896         "ps",
38897         "970"
38898       ],
38899       [
38900         "Panama (Panamá)",
38901         "pa",
38902         "507"
38903       ],
38904       [
38905         "Papua New Guinea",
38906         "pg",
38907         "675"
38908       ],
38909       [
38910         "Paraguay",
38911         "py",
38912         "595"
38913       ],
38914       [
38915         "Peru (Perú)",
38916         "pe",
38917         "51"
38918       ],
38919       [
38920         "Philippines",
38921         "ph",
38922         "63"
38923       ],
38924       [
38925         "Poland (Polska)",
38926         "pl",
38927         "48"
38928       ],
38929       [
38930         "Portugal",
38931         "pt",
38932         "351"
38933       ],
38934       [
38935         "Puerto Rico",
38936         "pr",
38937         "1",
38938         3,
38939         ["787", "939"]
38940       ],
38941       [
38942         "Qatar (‫قطر‬‎)",
38943         "qa",
38944         "974"
38945       ],
38946       [
38947         "Réunion (La Réunion)",
38948         "re",
38949         "262",
38950         0
38951       ],
38952       [
38953         "Romania (România)",
38954         "ro",
38955         "40"
38956       ],
38957       [
38958         "Russia (Россия)",
38959         "ru",
38960         "7",
38961         0
38962       ],
38963       [
38964         "Rwanda",
38965         "rw",
38966         "250"
38967       ],
38968       [
38969         "Saint Barthélemy",
38970         "bl",
38971         "590",
38972         1
38973       ],
38974       [
38975         "Saint Helena",
38976         "sh",
38977         "290"
38978       ],
38979       [
38980         "Saint Kitts and Nevis",
38981         "kn",
38982         "1869"
38983       ],
38984       [
38985         "Saint Lucia",
38986         "lc",
38987         "1758"
38988       ],
38989       [
38990         "Saint Martin (Saint-Martin (partie française))",
38991         "mf",
38992         "590",
38993         2
38994       ],
38995       [
38996         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38997         "pm",
38998         "508"
38999       ],
39000       [
39001         "Saint Vincent and the Grenadines",
39002         "vc",
39003         "1784"
39004       ],
39005       [
39006         "Samoa",
39007         "ws",
39008         "685"
39009       ],
39010       [
39011         "San Marino",
39012         "sm",
39013         "378"
39014       ],
39015       [
39016         "São Tomé and Príncipe (São Tomé e Príncipe)",
39017         "st",
39018         "239"
39019       ],
39020       [
39021         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39022         "sa",
39023         "966"
39024       ],
39025       [
39026         "Senegal (Sénégal)",
39027         "sn",
39028         "221"
39029       ],
39030       [
39031         "Serbia (Србија)",
39032         "rs",
39033         "381"
39034       ],
39035       [
39036         "Seychelles",
39037         "sc",
39038         "248"
39039       ],
39040       [
39041         "Sierra Leone",
39042         "sl",
39043         "232"
39044       ],
39045       [
39046         "Singapore",
39047         "sg",
39048         "65"
39049       ],
39050       [
39051         "Sint Maarten",
39052         "sx",
39053         "1721"
39054       ],
39055       [
39056         "Slovakia (Slovensko)",
39057         "sk",
39058         "421"
39059       ],
39060       [
39061         "Slovenia (Slovenija)",
39062         "si",
39063         "386"
39064       ],
39065       [
39066         "Solomon Islands",
39067         "sb",
39068         "677"
39069       ],
39070       [
39071         "Somalia (Soomaaliya)",
39072         "so",
39073         "252"
39074       ],
39075       [
39076         "South Africa",
39077         "za",
39078         "27"
39079       ],
39080       [
39081         "South Korea (대한민국)",
39082         "kr",
39083         "82"
39084       ],
39085       [
39086         "South Sudan (‫جنوب السودان‬‎)",
39087         "ss",
39088         "211"
39089       ],
39090       [
39091         "Spain (España)",
39092         "es",
39093         "34"
39094       ],
39095       [
39096         "Sri Lanka (ශ්‍රී ලංකාව)",
39097         "lk",
39098         "94"
39099       ],
39100       [
39101         "Sudan (‫السودان‬‎)",
39102         "sd",
39103         "249"
39104       ],
39105       [
39106         "Suriname",
39107         "sr",
39108         "597"
39109       ],
39110       [
39111         "Svalbard and Jan Mayen",
39112         "sj",
39113         "47",
39114         1
39115       ],
39116       [
39117         "Swaziland",
39118         "sz",
39119         "268"
39120       ],
39121       [
39122         "Sweden (Sverige)",
39123         "se",
39124         "46"
39125       ],
39126       [
39127         "Switzerland (Schweiz)",
39128         "ch",
39129         "41"
39130       ],
39131       [
39132         "Syria (‫سوريا‬‎)",
39133         "sy",
39134         "963"
39135       ],
39136       [
39137         "Taiwan (台灣)",
39138         "tw",
39139         "886"
39140       ],
39141       [
39142         "Tajikistan",
39143         "tj",
39144         "992"
39145       ],
39146       [
39147         "Tanzania",
39148         "tz",
39149         "255"
39150       ],
39151       [
39152         "Thailand (ไทย)",
39153         "th",
39154         "66"
39155       ],
39156       [
39157         "Timor-Leste",
39158         "tl",
39159         "670"
39160       ],
39161       [
39162         "Togo",
39163         "tg",
39164         "228"
39165       ],
39166       [
39167         "Tokelau",
39168         "tk",
39169         "690"
39170       ],
39171       [
39172         "Tonga",
39173         "to",
39174         "676"
39175       ],
39176       [
39177         "Trinidad and Tobago",
39178         "tt",
39179         "1868"
39180       ],
39181       [
39182         "Tunisia (‫تونس‬‎)",
39183         "tn",
39184         "216"
39185       ],
39186       [
39187         "Turkey (Türkiye)",
39188         "tr",
39189         "90"
39190       ],
39191       [
39192         "Turkmenistan",
39193         "tm",
39194         "993"
39195       ],
39196       [
39197         "Turks and Caicos Islands",
39198         "tc",
39199         "1649"
39200       ],
39201       [
39202         "Tuvalu",
39203         "tv",
39204         "688"
39205       ],
39206       [
39207         "U.S. Virgin Islands",
39208         "vi",
39209         "1340"
39210       ],
39211       [
39212         "Uganda",
39213         "ug",
39214         "256"
39215       ],
39216       [
39217         "Ukraine (Україна)",
39218         "ua",
39219         "380"
39220       ],
39221       [
39222         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39223         "ae",
39224         "971"
39225       ],
39226       [
39227         "United Kingdom",
39228         "gb",
39229         "44",
39230         0
39231       ],
39232       [
39233         "United States",
39234         "us",
39235         "1",
39236         0
39237       ],
39238       [
39239         "Uruguay",
39240         "uy",
39241         "598"
39242       ],
39243       [
39244         "Uzbekistan (Oʻzbekiston)",
39245         "uz",
39246         "998"
39247       ],
39248       [
39249         "Vanuatu",
39250         "vu",
39251         "678"
39252       ],
39253       [
39254         "Vatican City (Città del Vaticano)",
39255         "va",
39256         "39",
39257         1
39258       ],
39259       [
39260         "Venezuela",
39261         "ve",
39262         "58"
39263       ],
39264       [
39265         "Vietnam (Việt Nam)",
39266         "vn",
39267         "84"
39268       ],
39269       [
39270         "Wallis and Futuna (Wallis-et-Futuna)",
39271         "wf",
39272         "681"
39273       ],
39274       [
39275         "Western Sahara (‫الصحراء الغربية‬‎)",
39276         "eh",
39277         "212",
39278         1
39279       ],
39280       [
39281         "Yemen (‫اليمن‬‎)",
39282         "ye",
39283         "967"
39284       ],
39285       [
39286         "Zambia",
39287         "zm",
39288         "260"
39289       ],
39290       [
39291         "Zimbabwe",
39292         "zw",
39293         "263"
39294       ],
39295       [
39296         "Åland Islands",
39297         "ax",
39298         "358",
39299         1
39300       ]
39301   ];
39302   
39303   return d;
39304 }/**
39305 *    This script refer to:
39306 *    Title: International Telephone Input
39307 *    Author: Jack O'Connor
39308 *    Code version:  v12.1.12
39309 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39310 **/
39311
39312 /**
39313  * @class Roo.bootstrap.PhoneInput
39314  * @extends Roo.bootstrap.TriggerField
39315  * An input with International dial-code selection
39316  
39317  * @cfg {String} defaultDialCode default '+852'
39318  * @cfg {Array} preferedCountries default []
39319   
39320  * @constructor
39321  * Create a new PhoneInput.
39322  * @param {Object} config Configuration options
39323  */
39324
39325 Roo.bootstrap.PhoneInput = function(config) {
39326     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39327 };
39328
39329 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39330         
39331         listWidth: undefined,
39332         
39333         selectedClass: 'active',
39334         
39335         invalidClass : "has-warning",
39336         
39337         validClass: 'has-success',
39338         
39339         allowed: '0123456789',
39340         
39341         /**
39342          * @cfg {String} defaultDialCode The default dial code when initializing the input
39343          */
39344         defaultDialCode: '+852',
39345         
39346         /**
39347          * @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
39348          */
39349         preferedCountries: false,
39350         
39351         getAutoCreate : function()
39352         {
39353             var data = Roo.bootstrap.PhoneInputData();
39354             var align = this.labelAlign || this.parentLabelAlign();
39355             var id = Roo.id();
39356             
39357             this.allCountries = [];
39358             this.dialCodeMapping = [];
39359             
39360             for (var i = 0; i < data.length; i++) {
39361               var c = data[i];
39362               this.allCountries[i] = {
39363                 name: c[0],
39364                 iso2: c[1],
39365                 dialCode: c[2],
39366                 priority: c[3] || 0,
39367                 areaCodes: c[4] || null
39368               };
39369               this.dialCodeMapping[c[2]] = {
39370                   name: c[0],
39371                   iso2: c[1],
39372                   priority: c[3] || 0,
39373                   areaCodes: c[4] || null
39374               };
39375             }
39376             
39377             var cfg = {
39378                 cls: 'form-group',
39379                 cn: []
39380             };
39381             
39382             var input =  {
39383                 tag: 'input',
39384                 id : id,
39385                 cls : 'form-control tel-input',
39386                 autocomplete: 'new-password'
39387             };
39388             
39389             var hiddenInput = {
39390                 tag: 'input',
39391                 type: 'hidden',
39392                 cls: 'hidden-tel-input'
39393             };
39394             
39395             if (this.name) {
39396                 hiddenInput.name = this.name;
39397             }
39398             
39399             if (this.disabled) {
39400                 input.disabled = true;
39401             }
39402             
39403             var flag_container = {
39404                 tag: 'div',
39405                 cls: 'flag-box',
39406                 cn: [
39407                     {
39408                         tag: 'div',
39409                         cls: 'flag'
39410                     },
39411                     {
39412                         tag: 'div',
39413                         cls: 'caret'
39414                     }
39415                 ]
39416             };
39417             
39418             var box = {
39419                 tag: 'div',
39420                 cls: this.hasFeedback ? 'has-feedback' : '',
39421                 cn: [
39422                     hiddenInput,
39423                     input,
39424                     {
39425                         tag: 'input',
39426                         cls: 'dial-code-holder',
39427                         disabled: true
39428                     }
39429                 ]
39430             };
39431             
39432             var container = {
39433                 cls: 'roo-select2-container input-group',
39434                 cn: [
39435                     flag_container,
39436                     box
39437                 ]
39438             };
39439             
39440             if (this.fieldLabel.length) {
39441                 var indicator = {
39442                     tag: 'i',
39443                     tooltip: 'This field is required'
39444                 };
39445                 
39446                 var label = {
39447                     tag: 'label',
39448                     'for':  id,
39449                     cls: 'control-label',
39450                     cn: []
39451                 };
39452                 
39453                 var label_text = {
39454                     tag: 'span',
39455                     html: this.fieldLabel
39456                 };
39457                 
39458                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39459                 label.cn = [
39460                     indicator,
39461                     label_text
39462                 ];
39463                 
39464                 if(this.indicatorpos == 'right') {
39465                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39466                     label.cn = [
39467                         label_text,
39468                         indicator
39469                     ];
39470                 }
39471                 
39472                 if(align == 'left') {
39473                     container = {
39474                         tag: 'div',
39475                         cn: [
39476                             container
39477                         ]
39478                     };
39479                     
39480                     if(this.labelWidth > 12){
39481                         label.style = "width: " + this.labelWidth + 'px';
39482                     }
39483                     if(this.labelWidth < 13 && this.labelmd == 0){
39484                         this.labelmd = this.labelWidth;
39485                     }
39486                     if(this.labellg > 0){
39487                         label.cls += ' col-lg-' + this.labellg;
39488                         input.cls += ' col-lg-' + (12 - this.labellg);
39489                     }
39490                     if(this.labelmd > 0){
39491                         label.cls += ' col-md-' + this.labelmd;
39492                         container.cls += ' col-md-' + (12 - this.labelmd);
39493                     }
39494                     if(this.labelsm > 0){
39495                         label.cls += ' col-sm-' + this.labelsm;
39496                         container.cls += ' col-sm-' + (12 - this.labelsm);
39497                     }
39498                     if(this.labelxs > 0){
39499                         label.cls += ' col-xs-' + this.labelxs;
39500                         container.cls += ' col-xs-' + (12 - this.labelxs);
39501                     }
39502                 }
39503             }
39504             
39505             cfg.cn = [
39506                 label,
39507                 container
39508             ];
39509             
39510             var settings = this;
39511             
39512             ['xs','sm','md','lg'].map(function(size){
39513                 if (settings[size]) {
39514                     cfg.cls += ' col-' + size + '-' + settings[size];
39515                 }
39516             });
39517             
39518             this.store = new Roo.data.Store({
39519                 proxy : new Roo.data.MemoryProxy({}),
39520                 reader : new Roo.data.JsonReader({
39521                     fields : [
39522                         {
39523                             'name' : 'name',
39524                             'type' : 'string'
39525                         },
39526                         {
39527                             'name' : 'iso2',
39528                             'type' : 'string'
39529                         },
39530                         {
39531                             'name' : 'dialCode',
39532                             'type' : 'string'
39533                         },
39534                         {
39535                             'name' : 'priority',
39536                             'type' : 'string'
39537                         },
39538                         {
39539                             'name' : 'areaCodes',
39540                             'type' : 'string'
39541                         }
39542                     ]
39543                 })
39544             });
39545             
39546             if(!this.preferedCountries) {
39547                 this.preferedCountries = [
39548                     'hk',
39549                     'gb',
39550                     'us'
39551                 ];
39552             }
39553             
39554             var p = this.preferedCountries.reverse();
39555             
39556             if(p) {
39557                 for (var i = 0; i < p.length; i++) {
39558                     for (var j = 0; j < this.allCountries.length; j++) {
39559                         if(this.allCountries[j].iso2 == p[i]) {
39560                             var t = this.allCountries[j];
39561                             this.allCountries.splice(j,1);
39562                             this.allCountries.unshift(t);
39563                         }
39564                     } 
39565                 }
39566             }
39567             
39568             this.store.proxy.data = {
39569                 success: true,
39570                 data: this.allCountries
39571             };
39572             
39573             return cfg;
39574         },
39575         
39576         initEvents : function()
39577         {
39578             this.createList();
39579             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39580             
39581             this.indicator = this.indicatorEl();
39582             this.flag = this.flagEl();
39583             this.dialCodeHolder = this.dialCodeHolderEl();
39584             
39585             this.trigger = this.el.select('div.flag-box',true).first();
39586             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39587             
39588             var _this = this;
39589             
39590             (function(){
39591                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39592                 _this.list.setWidth(lw);
39593             }).defer(100);
39594             
39595             this.list.on('mouseover', this.onViewOver, this);
39596             this.list.on('mousemove', this.onViewMove, this);
39597             this.inputEl().on("keyup", this.onKeyUp, this);
39598             
39599             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39600
39601             this.view = new Roo.View(this.list, this.tpl, {
39602                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39603             });
39604             
39605             this.view.on('click', this.onViewClick, this);
39606             this.setValue(this.defaultDialCode);
39607         },
39608         
39609         onTriggerClick : function(e)
39610         {
39611             Roo.log('trigger click');
39612             if(this.disabled){
39613                 return;
39614             }
39615             
39616             if(this.isExpanded()){
39617                 this.collapse();
39618                 this.hasFocus = false;
39619             }else {
39620                 this.store.load({});
39621                 this.hasFocus = true;
39622                 this.expand();
39623             }
39624         },
39625         
39626         isExpanded : function()
39627         {
39628             return this.list.isVisible();
39629         },
39630         
39631         collapse : function()
39632         {
39633             if(!this.isExpanded()){
39634                 return;
39635             }
39636             this.list.hide();
39637             Roo.get(document).un('mousedown', this.collapseIf, this);
39638             Roo.get(document).un('mousewheel', this.collapseIf, this);
39639             this.fireEvent('collapse', this);
39640             this.validate();
39641         },
39642         
39643         expand : function()
39644         {
39645             Roo.log('expand');
39646
39647             if(this.isExpanded() || !this.hasFocus){
39648                 return;
39649             }
39650             
39651             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39652             this.list.setWidth(lw);
39653             
39654             this.list.show();
39655             this.restrictHeight();
39656             
39657             Roo.get(document).on('mousedown', this.collapseIf, this);
39658             Roo.get(document).on('mousewheel', this.collapseIf, this);
39659             
39660             this.fireEvent('expand', this);
39661         },
39662         
39663         restrictHeight : function()
39664         {
39665             this.list.alignTo(this.inputEl(), this.listAlign);
39666             this.list.alignTo(this.inputEl(), this.listAlign);
39667         },
39668         
39669         onViewOver : function(e, t)
39670         {
39671             if(this.inKeyMode){
39672                 return;
39673             }
39674             var item = this.view.findItemFromChild(t);
39675             
39676             if(item){
39677                 var index = this.view.indexOf(item);
39678                 this.select(index, false);
39679             }
39680         },
39681
39682         // private
39683         onViewClick : function(view, doFocus, el, e)
39684         {
39685             var index = this.view.getSelectedIndexes()[0];
39686             
39687             var r = this.store.getAt(index);
39688             
39689             if(r){
39690                 this.onSelect(r, index);
39691             }
39692             if(doFocus !== false && !this.blockFocus){
39693                 this.inputEl().focus();
39694             }
39695         },
39696         
39697         onViewMove : function(e, t)
39698         {
39699             this.inKeyMode = false;
39700         },
39701         
39702         select : function(index, scrollIntoView)
39703         {
39704             this.selectedIndex = index;
39705             this.view.select(index);
39706             if(scrollIntoView !== false){
39707                 var el = this.view.getNode(index);
39708                 if(el){
39709                     this.list.scrollChildIntoView(el, false);
39710                 }
39711             }
39712         },
39713         
39714         createList : function()
39715         {
39716             this.list = Roo.get(document.body).createChild({
39717                 tag: 'ul',
39718                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39719                 style: 'display:none'
39720             });
39721             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39722         },
39723         
39724         collapseIf : function(e)
39725         {
39726             var in_combo  = e.within(this.el);
39727             var in_list =  e.within(this.list);
39728             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39729             
39730             if (in_combo || in_list || is_list) {
39731                 return;
39732             }
39733             this.collapse();
39734         },
39735         
39736         onSelect : function(record, index)
39737         {
39738             if(this.fireEvent('beforeselect', this, record, index) !== false){
39739                 
39740                 this.setFlagClass(record.data.iso2);
39741                 this.setDialCode(record.data.dialCode);
39742                 this.hasFocus = false;
39743                 this.collapse();
39744                 this.fireEvent('select', this, record, index);
39745             }
39746         },
39747         
39748         flagEl : function()
39749         {
39750             var flag = this.el.select('div.flag',true).first();
39751             if(!flag){
39752                 return false;
39753             }
39754             return flag;
39755         },
39756         
39757         dialCodeHolderEl : function()
39758         {
39759             var d = this.el.select('input.dial-code-holder',true).first();
39760             if(!d){
39761                 return false;
39762             }
39763             return d;
39764         },
39765         
39766         setDialCode : function(v)
39767         {
39768             this.dialCodeHolder.dom.value = '+'+v;
39769         },
39770         
39771         setFlagClass : function(n)
39772         {
39773             this.flag.dom.className = 'flag '+n;
39774         },
39775         
39776         getValue : function()
39777         {
39778             var v = this.inputEl().getValue();
39779             if(this.dialCodeHolder) {
39780                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39781             }
39782             return v;
39783         },
39784         
39785         setValue : function(v)
39786         {
39787             var d = this.getDialCode(v);
39788             
39789             //invalid dial code
39790             if(v.length == 0 || !d || d.length == 0) {
39791                 if(this.rendered){
39792                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39793                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39794                 }
39795                 return;
39796             }
39797             
39798             //valid dial code
39799             this.setFlagClass(this.dialCodeMapping[d].iso2);
39800             this.setDialCode(d);
39801             this.inputEl().dom.value = v.replace('+'+d,'');
39802             this.hiddenEl().dom.value = this.getValue();
39803             
39804             this.validate();
39805         },
39806         
39807         getDialCode : function(v = '')
39808         {
39809             if (v.length == 0) {
39810                 return this.dialCodeHolder.dom.value;
39811             }
39812             
39813             var dialCode = "";
39814             if (v.charAt(0) != "+") {
39815                 return false;
39816             }
39817             var numericChars = "";
39818             for (var i = 1; i < v.length; i++) {
39819               var c = v.charAt(i);
39820               if (!isNaN(c)) {
39821                 numericChars += c;
39822                 if (this.dialCodeMapping[numericChars]) {
39823                   dialCode = v.substr(1, i);
39824                 }
39825                 if (numericChars.length == 4) {
39826                   break;
39827                 }
39828               }
39829             }
39830             return dialCode;
39831         },
39832         
39833         reset : function()
39834         {
39835             this.setValue(this.defaultDialCode);
39836             this.validate();
39837         },
39838         
39839         hiddenEl : function()
39840         {
39841             return this.el.select('input.hidden-tel-input',true).first();
39842         },
39843         
39844         onKeyUp : function(e){
39845             
39846             var k = e.getKey();
39847             var c = e.getCharCode();
39848             
39849             if(
39850                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39851                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39852             ){
39853                 e.stopEvent();
39854             }
39855             
39856             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39857             //     return;
39858             // }
39859             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39860                 e.stopEvent();
39861             }
39862             
39863             this.setValue(this.getValue());
39864         }
39865         
39866 });
39867 /**
39868  * @class Roo.bootstrap.MoneyField
39869  * @extends Roo.bootstrap.ComboBox
39870  * Bootstrap MoneyField class
39871  * 
39872  * @constructor
39873  * Create a new MoneyField.
39874  * @param {Object} config Configuration options
39875  */
39876
39877 Roo.bootstrap.MoneyField = function(config) {
39878     
39879     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39880     
39881 };
39882
39883 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39884     
39885     /**
39886      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39887      */
39888     allowDecimals : true,
39889     /**
39890      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39891      */
39892     decimalSeparator : ".",
39893     /**
39894      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39895      */
39896     decimalPrecision : 2,
39897     /**
39898      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39899      */
39900     allowNegative : true,
39901     /**
39902      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39903      */
39904     minValue : Number.NEGATIVE_INFINITY,
39905     /**
39906      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39907      */
39908     maxValue : Number.MAX_VALUE,
39909     /**
39910      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39911      */
39912     minText : "The minimum value for this field is {0}",
39913     /**
39914      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39915      */
39916     maxText : "The maximum value for this field is {0}",
39917     /**
39918      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39919      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39920      */
39921     nanText : "{0} is not a valid number",
39922     /**
39923      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39924      */
39925     castInt : true,
39926     
39927     inputlg : 9,
39928     inputmd : 9,
39929     inputsm : 9,
39930     inputxs : 6,
39931     
39932     store : false,
39933     
39934     getAutoCreate : function()
39935     {
39936         var align = this.labelAlign || this.parentLabelAlign();
39937         
39938         var id = Roo.id();
39939
39940         var cfg = {
39941             cls: 'form-group',
39942             cn: []
39943         };
39944
39945         var input =  {
39946             tag: 'input',
39947             id : id,
39948             cls : 'form-control roo-money-amount-input',
39949             autocomplete: 'new-password'
39950         };
39951         
39952         if (this.name) {
39953             input.name = this.name;
39954         }
39955
39956         if (this.disabled) {
39957             input.disabled = true;
39958         }
39959
39960         var clg = 12 - this.inputlg;
39961         var cmd = 12 - this.inputmd;
39962         var csm = 12 - this.inputsm;
39963         var cxs = 12 - this.inputxs;
39964         
39965         var container = {
39966             tag : 'div',
39967             cls : 'row roo-money-field',
39968             cn : [
39969                 {
39970                     tag : 'div',
39971                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39972                     cn : [
39973                         {
39974                             tag : 'div',
39975                             cls: 'roo-select2-container input-group',
39976                             cn: [
39977                                 {
39978                                     tag : 'input',
39979                                     cls : 'form-control roo-money-currency-input',
39980                                     autocomplete: 'new-password',
39981                                     readOnly : 1,
39982                                     name : this.currencyName
39983                                 },
39984                                 {
39985                                     tag :'span',
39986                                     cls : 'input-group-addon',
39987                                     cn : [
39988                                         {
39989                                             tag: 'span',
39990                                             cls: 'caret'
39991                                         }
39992                                     ]
39993                                 }
39994                             ]
39995                         }
39996                     ]
39997                 },
39998                 {
39999                     tag : 'div',
40000                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40001                     cn : [
40002                         {
40003                             tag: 'div',
40004                             cls: this.hasFeedback ? 'has-feedback' : '',
40005                             cn: [
40006                                 input
40007                             ]
40008                         }
40009                     ]
40010                 }
40011             ]
40012             
40013         };
40014         
40015         if (this.fieldLabel.length) {
40016             var indicator = {
40017                 tag: 'i',
40018                 tooltip: 'This field is required'
40019             };
40020
40021             var label = {
40022                 tag: 'label',
40023                 'for':  id,
40024                 cls: 'control-label',
40025                 cn: []
40026             };
40027
40028             var label_text = {
40029                 tag: 'span',
40030                 html: this.fieldLabel
40031             };
40032
40033             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40034             label.cn = [
40035                 indicator,
40036                 label_text
40037             ];
40038
40039             if(this.indicatorpos == 'right') {
40040                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40041                 label.cn = [
40042                     label_text,
40043                     indicator
40044                 ];
40045             }
40046
40047             if(align == 'left') {
40048                 container = {
40049                     tag: 'div',
40050                     cn: [
40051                         container
40052                     ]
40053                 };
40054
40055                 if(this.labelWidth > 12){
40056                     label.style = "width: " + this.labelWidth + 'px';
40057                 }
40058                 if(this.labelWidth < 13 && this.labelmd == 0){
40059                     this.labelmd = this.labelWidth;
40060                 }
40061                 if(this.labellg > 0){
40062                     label.cls += ' col-lg-' + this.labellg;
40063                     input.cls += ' col-lg-' + (12 - this.labellg);
40064                 }
40065                 if(this.labelmd > 0){
40066                     label.cls += ' col-md-' + this.labelmd;
40067                     container.cls += ' col-md-' + (12 - this.labelmd);
40068                 }
40069                 if(this.labelsm > 0){
40070                     label.cls += ' col-sm-' + this.labelsm;
40071                     container.cls += ' col-sm-' + (12 - this.labelsm);
40072                 }
40073                 if(this.labelxs > 0){
40074                     label.cls += ' col-xs-' + this.labelxs;
40075                     container.cls += ' col-xs-' + (12 - this.labelxs);
40076                 }
40077             }
40078         }
40079
40080         cfg.cn = [
40081             label,
40082             container
40083         ];
40084
40085         var settings = this;
40086
40087         ['xs','sm','md','lg'].map(function(size){
40088             if (settings[size]) {
40089                 cfg.cls += ' col-' + size + '-' + settings[size];
40090             }
40091         });
40092         
40093         return cfg;
40094         
40095     },
40096     
40097     initEvents : function()
40098     {
40099         this.indicator = this.indicatorEl();
40100         
40101         this.initCurrencyEvent();
40102         
40103         this.initNumberEvent();
40104         
40105     },
40106     
40107     initCurrencyEvent : function()
40108     {
40109         if (!this.store) {
40110             throw "can not find store for combo";
40111         }
40112         
40113         this.store = Roo.factory(this.store, Roo.data);
40114         this.store.parent = this;
40115         
40116         this.createList();
40117         
40118         this.triggerEl = this.el.select('.input-group-addon', true).first();
40119         
40120         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40121         
40122         var _this = this;
40123         
40124         (function(){
40125             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40126             _this.list.setWidth(lw);
40127         }).defer(100);
40128         
40129         this.list.on('mouseover', this.onViewOver, this);
40130         this.list.on('mousemove', this.onViewMove, this);
40131         this.list.on('scroll', this.onViewScroll, this);
40132         
40133         if(!this.tpl){
40134             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40135         }
40136         
40137         this.view = new Roo.View(this.list, this.tpl, {
40138             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40139         });
40140         
40141         this.view.on('click', this.onViewClick, this);
40142         
40143         this.store.on('beforeload', this.onBeforeLoad, this);
40144         this.store.on('load', this.onLoad, this);
40145         this.store.on('loadexception', this.onLoadException, this);
40146         
40147         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40148             "up" : function(e){
40149                 this.inKeyMode = true;
40150                 this.selectPrev();
40151             },
40152
40153             "down" : function(e){
40154                 if(!this.isExpanded()){
40155                     this.onTriggerClick();
40156                 }else{
40157                     this.inKeyMode = true;
40158                     this.selectNext();
40159                 }
40160             },
40161
40162             "enter" : function(e){
40163                 this.collapse();
40164                 
40165                 if(this.fireEvent("specialkey", this, e)){
40166                     this.onViewClick(false);
40167                 }
40168                 
40169                 return true;
40170             },
40171
40172             "esc" : function(e){
40173                 this.collapse();
40174             },
40175
40176             "tab" : function(e){
40177                 this.collapse();
40178                 
40179                 if(this.fireEvent("specialkey", this, e)){
40180                     this.onViewClick(false);
40181                 }
40182                 
40183                 return true;
40184             },
40185
40186             scope : this,
40187
40188             doRelay : function(foo, bar, hname){
40189                 if(hname == 'down' || this.scope.isExpanded()){
40190                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40191                 }
40192                 return true;
40193             },
40194
40195             forceKeyDown: true
40196         });
40197         
40198         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40199         
40200     },
40201     
40202     initNumberEvent : function(e)
40203     {
40204         this.inputEl().on("keydown" , this.fireKey,  this);
40205         this.inputEl().on("focus", this.onFocus,  this);
40206         this.inputEl().on("blur", this.onBlur,  this);
40207         
40208         this.inputEl().relayEvent('keyup', this);
40209         
40210         if(this.indicator){
40211             this.indicator.addClass('invisible');
40212         }
40213  
40214         this.originalValue = this.getValue();
40215         
40216         if(this.validationEvent == 'keyup'){
40217             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40218             this.inputEl().on('keyup', this.filterValidation, this);
40219         }
40220         else if(this.validationEvent !== false){
40221             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40222         }
40223         
40224         if(this.selectOnFocus){
40225             this.on("focus", this.preFocus, this);
40226             
40227         }
40228         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40229             this.inputEl().on("keypress", this.filterKeys, this);
40230         } else {
40231             this.inputEl().relayEvent('keypress', this);
40232         }
40233         
40234         var allowed = "0123456789";
40235         
40236         if(this.allowDecimals){
40237             allowed += this.decimalSeparator;
40238         }
40239         
40240         if(this.allowNegative){
40241             allowed += "-";
40242         }
40243         
40244         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40245         
40246         var keyPress = function(e){
40247             
40248             var k = e.getKey();
40249             
40250             var c = e.getCharCode();
40251             
40252             if(
40253                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40254                     allowed.indexOf(String.fromCharCode(c)) === -1
40255             ){
40256                 e.stopEvent();
40257                 return;
40258             }
40259             
40260             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40261                 return;
40262             }
40263             
40264             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40265                 e.stopEvent();
40266             }
40267         };
40268         
40269         this.inputEl().on("keypress", keyPress, this);
40270         
40271     },
40272     
40273     onTriggerClick : function(e)
40274     {   
40275         if(this.disabled){
40276             return;
40277         }
40278         
40279         this.page = 0;
40280         this.loadNext = false;
40281         
40282         if(this.isExpanded()){
40283             this.collapse();
40284             return;
40285         }
40286         
40287         this.hasFocus = true;
40288         
40289         if(this.triggerAction == 'all') {
40290             this.doQuery(this.allQuery, true);
40291             return;
40292         }
40293         
40294         this.doQuery(this.getRawValue());
40295     },
40296     
40297     getCurrency : function()
40298     {   
40299         var v = this.currencyEl().getValue();
40300         
40301         return v;
40302     },
40303     
40304     restrictHeight : function()
40305     {
40306         this.list.alignTo(this.currencyEl(), this.listAlign);
40307         this.list.alignTo(this.currencyEl(), this.listAlign);
40308     },
40309     
40310     onViewClick : function(view, doFocus, el, e)
40311     {
40312         var index = this.view.getSelectedIndexes()[0];
40313         
40314         var r = this.store.getAt(index);
40315         
40316         if(r){
40317             this.onSelect(r, index);
40318         }
40319     },
40320     
40321     onSelect : function(record, index){
40322         
40323         if(this.fireEvent('beforeselect', this, record, index) !== false){
40324         
40325             this.setFromCurrencyData(index > -1 ? record.data : false);
40326             
40327             this.collapse();
40328             
40329             this.fireEvent('select', this, record, index);
40330         }
40331     },
40332     
40333     setFromCurrencyData : function(o)
40334     {
40335         var currency = '';
40336         
40337         this.lastCurrency = o;
40338         
40339         if (this.currencyField) {
40340             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40341         } else {
40342             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40343         }
40344         
40345         this.lastSelectionText = currency;
40346         
40347         this.setCurrency(currency);
40348     },
40349     
40350     setFromData : function(o)
40351     {
40352         var c = {};
40353         
40354         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40355         
40356         this.setFromCurrencyData(c);
40357         
40358         var value = '';
40359         
40360         if (this.name) {
40361             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40362         } else {
40363             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40364         }
40365         
40366         this.setValue(value);
40367         
40368     },
40369     
40370     setCurrency : function(v)
40371     {   
40372         this.currencyValue = v;
40373         
40374         if(this.rendered){
40375             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40376             this.validate();
40377         }
40378     },
40379     
40380     setValue : function(v)
40381     {
40382         v = this.fixPrecision(v);
40383         
40384         v = String(v).replace(".", this.decimalSeparator);
40385         
40386         this.value = v;
40387         
40388         if(this.rendered){
40389             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40390             this.validate();
40391         }
40392     },
40393     
40394     getRawValue : function()
40395     {
40396         var v = this.inputEl().getValue();
40397         
40398         return v;
40399     },
40400     
40401     getValue : function()
40402     {
40403         return this.fixPrecision(this.parseValue(this.getRawValue()));
40404     },
40405     
40406     parseValue : function(value)
40407     {
40408         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40409         return isNaN(value) ? '' : value;
40410     },
40411     
40412     fixPrecision : function(value)
40413     {
40414         var nan = isNaN(value);
40415         
40416         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40417             return nan ? '' : value;
40418         }
40419         
40420         return parseFloat(value).toFixed(this.decimalPrecision);
40421     },
40422     
40423     decimalPrecisionFcn : function(v)
40424     {
40425         return Math.floor(v);
40426     },
40427     
40428     validateValue : function(value)
40429     {
40430         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40431             return false;
40432         }
40433         
40434         var num = this.parseValue(value);
40435         
40436         if(isNaN(num)){
40437             this.markInvalid(String.format(this.nanText, value));
40438             return false;
40439         }
40440         
40441         if(num < this.minValue){
40442             this.markInvalid(String.format(this.minText, this.minValue));
40443             return false;
40444         }
40445         
40446         if(num > this.maxValue){
40447             this.markInvalid(String.format(this.maxText, this.maxValue));
40448             return false;
40449         }
40450         
40451         return true;
40452     },
40453     
40454     validate : function()
40455     {
40456         if(this.disabled || this.allowBlank){
40457             this.markValid();
40458             return true;
40459         }
40460         
40461         var currency = this.getCurrency();
40462         
40463         if(this.validateValue(this.getRawValue()) && currency.length){
40464             this.markValid();
40465             return true;
40466         }
40467         
40468         this.markInvalid();
40469         return false;
40470     },
40471     
40472     getName: function()
40473     {
40474         return this.name;
40475     },
40476     
40477     beforeBlur : function()
40478     {
40479         if(!this.castInt){
40480             return;
40481         }
40482         
40483         var v = this.parseValue(this.getRawValue());
40484         
40485         if(v){
40486             this.setValue(v);
40487         }
40488     },
40489     
40490     onBlur : function()
40491     {
40492         this.beforeBlur();
40493         
40494         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40495             //this.el.removeClass(this.focusClass);
40496         }
40497         
40498         this.hasFocus = false;
40499         
40500         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40501             this.validate();
40502         }
40503         
40504         var v = this.getValue();
40505         
40506         if(String(v) !== String(this.startValue)){
40507             this.fireEvent('change', this, v, this.startValue);
40508         }
40509         
40510         this.fireEvent("blur", this);
40511     },
40512     
40513     inputEl : function()
40514     {
40515         return this.el.select('.roo-money-amount-input', true).first();
40516     },
40517     
40518     currencyEl : function()
40519     {
40520         return this.el.select('.roo-money-currency-input', true).first();
40521     }
40522     
40523 });