4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
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)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
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];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
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);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
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') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
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.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
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);
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);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
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)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
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])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
395 config = config || {};
397 Roo.bootstrap.Body.superclass.constructor.call(this, config);
398 this.el = Roo.get(config.el ? config.el : document.body );
399 if (this.cls && this.cls.length) {
400 Roo.get(document.body).addClass(this.cls);
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
406 is_body : true,// just to make sure it's constructed?
411 onRender : function(ct, position)
413 /* Roo.log("Roo.bootstrap.Body - onRender");
414 if (this.cls && this.cls.length) {
415 Roo.get(document.body).addClass(this.cls);
434 * @class Roo.bootstrap.ButtonGroup
435 * @extends Roo.bootstrap.Component
436 * Bootstrap ButtonGroup class
437 * @cfg {String} size lg | sm | xs (default empty normal)
438 * @cfg {String} align vertical | justified (default none)
439 * @cfg {String} direction up | down (default down)
440 * @cfg {Boolean} toolbar false | true
441 * @cfg {Boolean} btn true | false
446 * @param {Object} config The config object
449 Roo.bootstrap.ButtonGroup = function(config){
450 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
461 getAutoCreate : function(){
467 cfg.html = this.html || cfg.html;
478 if (['vertical','justified'].indexOf(this.align)!==-1) {
479 cfg.cls = 'btn-group-' + this.align;
481 if (this.align == 'justified') {
482 console.log(this.items);
486 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487 cfg.cls += ' btn-group-' + this.size;
490 if (this.direction == 'up') {
491 cfg.cls += ' dropup' ;
507 * @class Roo.bootstrap.Button
508 * @extends Roo.bootstrap.Component
509 * Bootstrap Button class
510 * @cfg {String} html The button content
511 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
512 * @cfg {String} size ( lg | sm | xs)
513 * @cfg {String} tag ( a | input | submit)
514 * @cfg {String} href empty or href
515 * @cfg {Boolean} disabled default false;
516 * @cfg {Boolean} isClose default false;
517 * @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)
518 * @cfg {String} badge text for badge
519 * @cfg {String} theme default
520 * @cfg {Boolean} inverse
521 * @cfg {Boolean} toggle
522 * @cfg {String} ontext text for on toggle state
523 * @cfg {String} offtext text for off toggle state
524 * @cfg {Boolean} defaulton
525 * @cfg {Boolean} preventDefault default true
526 * @cfg {Boolean} removeClass remove the standard class..
527 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
530 * Create a new button
531 * @param {Object} config The config object
535 Roo.bootstrap.Button = function(config){
536 Roo.bootstrap.Button.superclass.constructor.call(this, config);
541 * When a butotn is pressed
542 * @param {Roo.bootstrap.Button} this
543 * @param {Roo.EventObject} e
548 * After the button has been toggles
549 * @param {Roo.EventObject} e
550 * @param {boolean} pressed (also available as button.pressed)
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
574 preventDefault: true,
583 getAutoCreate : function(){
591 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
597 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
599 if (this.toggle == true) {
602 cls: 'slider-frame roo-button',
607 'data-off-text':'OFF',
608 cls: 'slider-button',
614 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615 cfg.cls += ' '+this.weight;
624 cfg["aria-hidden"] = true;
626 cfg.html = "×";
632 if (this.theme==='default') {
633 cfg.cls = 'btn roo-button';
635 //if (this.parentType != 'Navbar') {
636 this.weight = this.weight.length ? this.weight : 'default';
638 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
640 cfg.cls += ' btn-' + this.weight;
642 } else if (this.theme==='glow') {
645 cfg.cls = 'btn-glow roo-button';
647 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
649 cfg.cls += ' ' + this.weight;
655 this.cls += ' inverse';
660 cfg.cls += ' active';
664 cfg.disabled = 'disabled';
668 Roo.log('changing to ul' );
670 this.glyphicon = 'caret';
673 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
675 //gsRoo.log(this.parentType);
676 if (this.parentType === 'Navbar' && !this.parent().bar) {
677 Roo.log('changing to li?');
686 href : this.href || '#'
689 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
690 cfg.cls += ' dropdown';
697 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
699 if (this.glyphicon) {
700 cfg.html = ' ' + cfg.html;
705 cls: 'glyphicon glyphicon-' + this.glyphicon
715 // cfg.cls='btn roo-button';
719 var value = cfg.html;
724 cls: 'glyphicon glyphicon-' + this.glyphicon,
743 cfg.cls += ' dropdown';
744 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
747 if (cfg.tag !== 'a' && this.href !== '') {
748 throw "Tag must be a to set href.";
749 } else if (this.href.length > 0) {
750 cfg.href = this.href;
753 if(this.removeClass){
758 cfg.target = this.target;
763 initEvents: function() {
764 // Roo.log('init events?');
765 // Roo.log(this.el.dom);
768 if (typeof (this.menu) != 'undefined') {
769 this.menu.parentType = this.xtype;
770 this.menu.triggerEl = this.el;
771 this.addxtype(Roo.apply({}, this.menu));
775 if (this.el.hasClass('roo-button')) {
776 this.el.on('click', this.onClick, this);
778 this.el.select('.roo-button').on('click', this.onClick, this);
781 if(this.removeClass){
782 this.el.on('click', this.onClick, this);
785 this.el.enableDisplayMode();
788 onClick : function(e)
795 Roo.log('button on click ');
796 if(this.preventDefault){
799 if (this.pressed === true || this.pressed === false) {
800 this.pressed = !this.pressed;
801 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802 this.fireEvent('toggle', this, e, this.pressed);
806 this.fireEvent('click', this, e);
810 * Enables this button
814 this.disabled = false;
815 this.el.removeClass('disabled');
819 * Disable this button
823 this.disabled = true;
824 this.el.addClass('disabled');
827 * sets the active state on/off,
828 * @param {Boolean} state (optional) Force a particular state
830 setActive : function(v) {
832 this.el[v ? 'addClass' : 'removeClass']('active');
835 * toggles the current active state
837 toggleActive : function()
839 var active = this.el.hasClass('active');
840 this.setActive(!active);
844 setText : function(str)
846 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
850 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
873 * @class Roo.bootstrap.Column
874 * @extends Roo.bootstrap.Component
875 * Bootstrap Column class
876 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
886 * @cfg {Boolean} hidden (true|false) hide the element
887 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888 * @cfg {String} fa (ban|check|...) font awesome icon
889 * @cfg {Number} fasize (1|2|....) font awsome size
891 * @cfg {String} icon (info-sign|check|...) glyphicon name
893 * @cfg {String} html content of column.
896 * Create a new Column
897 * @param {Object} config The config object
900 Roo.bootstrap.Column = function(config){
901 Roo.bootstrap.Column.superclass.constructor.call(this, config);
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
922 getAutoCreate : function(){
923 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
931 ['xs','sm','md','lg'].map(function(size){
932 //Roo.log( size + ':' + settings[size]);
934 if (settings[size+'off'] !== false) {
935 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
938 if (settings[size] === false) {
942 if (!settings[size]) { // 0 = hidden
943 cfg.cls += ' hidden-' + size;
946 cfg.cls += ' col-' + size + '-' + settings[size];
951 cfg.cls += ' hidden';
954 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955 cfg.cls +=' alert alert-' + this.alert;
959 if (this.html.length) {
960 cfg.html = this.html;
964 if (this.fasize > 1) {
965 fasize = ' fa-' + this.fasize + 'x';
967 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
972 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
991 * @class Roo.bootstrap.Container
992 * @extends Roo.bootstrap.Component
993 * Bootstrap Container class
994 * @cfg {Boolean} jumbotron is it a jumbotron element
995 * @cfg {String} html content of element
996 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
998 * @cfg {String} header content of header (for panel)
999 * @cfg {String} footer content of footer (for panel)
1000 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001 * @cfg {String} tag (header|aside|section) type of HTML tag.
1002 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003 * @cfg {String} fa font awesome icon
1004 * @cfg {String} icon (info-sign|check|...) glyphicon name
1005 * @cfg {Boolean} hidden (true|false) hide the element
1006 * @cfg {Boolean} expandable (true|false) default false
1007 * @cfg {Boolean} expanded (true|false) default true
1008 * @cfg {String} rheader contet on the right of header
1009 * @cfg {Boolean} clickable (true|false) default false
1013 * Create a new Container
1014 * @param {Object} config The config object
1017 Roo.bootstrap.Container = function(config){
1018 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1024 * After the panel has been expand
1026 * @param {Roo.bootstrap.Container} this
1031 * After the panel has been collapsed
1033 * @param {Roo.bootstrap.Container} this
1038 * When a element is chick
1039 * @param {Roo.bootstrap.Container} this
1040 * @param {Roo.EventObject} e
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1064 getChildContainer : function() {
1070 if (this.panel.length) {
1071 return this.el.select('.panel-body',true).first();
1078 getAutoCreate : function(){
1081 tag : this.tag || 'div',
1085 if (this.jumbotron) {
1086 cfg.cls = 'jumbotron';
1091 // - this is applied by the parent..
1093 // cfg.cls = this.cls + '';
1096 if (this.sticky.length) {
1098 var bd = Roo.get(document.body);
1099 if (!bd.hasClass('bootstrap-sticky')) {
1100 bd.addClass('bootstrap-sticky');
1101 Roo.select('html',true).setStyle('height', '100%');
1104 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1108 if (this.well.length) {
1109 switch (this.well) {
1112 cfg.cls +=' well well-' +this.well;
1121 cfg.cls += ' hidden';
1125 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126 cfg.cls +=' alert alert-' + this.alert;
1131 if (this.panel.length) {
1132 cfg.cls += ' panel panel-' + this.panel;
1134 if (this.header.length) {
1138 if(this.expandable){
1140 cfg.cls = cfg.cls + ' expandable';
1144 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1152 cls : 'panel-title',
1153 html : (this.expandable ? ' ' : '') + this.header
1157 cls: 'panel-header-right',
1163 cls : 'panel-heading',
1164 style : this.expandable ? 'cursor: pointer' : '',
1172 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1177 if (this.footer.length) {
1179 cls : 'panel-footer',
1188 body.html = this.html || cfg.html;
1189 // prefix with the icons..
1191 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1194 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1199 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200 cfg.cls = 'container';
1206 initEvents: function()
1208 if(this.expandable){
1209 var headerEl = this.headerEl();
1212 headerEl.on('click', this.onToggleClick, this);
1217 this.el.on('click', this.onClick, this);
1222 onToggleClick : function()
1224 var headerEl = this.headerEl();
1240 if(this.fireEvent('expand', this)) {
1242 this.expanded = true;
1244 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1246 this.el.select('.panel-body',true).first().removeClass('hide');
1248 var toggleEl = this.toggleEl();
1254 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1259 collapse : function()
1261 if(this.fireEvent('collapse', this)) {
1263 this.expanded = false;
1265 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266 this.el.select('.panel-body',true).first().addClass('hide');
1268 var toggleEl = this.toggleEl();
1274 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1278 toggleEl : function()
1280 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1284 return this.el.select('.panel-heading .fa',true).first();
1287 headerEl : function()
1289 if(!this.el || !this.panel.length || !this.header.length){
1293 return this.el.select('.panel-heading',true).first()
1296 titleEl : function()
1298 if(!this.el || !this.panel.length || !this.header.length){
1302 return this.el.select('.panel-title',true).first();
1305 setTitle : function(v)
1307 var titleEl = this.titleEl();
1313 titleEl.dom.innerHTML = v;
1316 getTitle : function()
1319 var titleEl = this.titleEl();
1325 return titleEl.dom.innerHTML;
1328 setRightTitle : function(v)
1330 var t = this.el.select('.panel-header-right',true).first();
1336 t.dom.innerHTML = v;
1339 onClick : function(e)
1343 this.fireEvent('click', this, e);
1357 * @class Roo.bootstrap.Img
1358 * @extends Roo.bootstrap.Component
1359 * Bootstrap Img class
1360 * @cfg {Boolean} imgResponsive false | true
1361 * @cfg {String} border rounded | circle | thumbnail
1362 * @cfg {String} src image source
1363 * @cfg {String} alt image alternative text
1364 * @cfg {String} href a tag href
1365 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366 * @cfg {String} xsUrl xs image source
1367 * @cfg {String} smUrl sm image source
1368 * @cfg {String} mdUrl md image source
1369 * @cfg {String} lgUrl lg image source
1372 * Create a new Input
1373 * @param {Object} config The config object
1376 Roo.bootstrap.Img = function(config){
1377 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1383 * The img click event for the img.
1384 * @param {Roo.EventObject} e
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1392 imgResponsive: true,
1402 getAutoCreate : function()
1404 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405 return this.createSingleImg();
1410 cls: 'roo-image-responsive-group',
1415 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1417 if(!_this[size + 'Url']){
1423 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424 html: _this.html || cfg.html,
1425 src: _this[size + 'Url']
1428 img.cls += ' roo-image-responsive-' + size;
1430 var s = ['xs', 'sm', 'md', 'lg'];
1432 s.splice(s.indexOf(size), 1);
1434 Roo.each(s, function(ss){
1435 img.cls += ' hidden-' + ss;
1438 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439 cfg.cls += ' img-' + _this.border;
1443 cfg.alt = _this.alt;
1456 a.target = _this.target;
1460 cfg.cn.push((_this.href) ? a : img);
1467 createSingleImg : function()
1471 cls: (this.imgResponsive) ? 'img-responsive' : '',
1473 src : 'about:blank' // just incase src get's set to undefined?!?
1476 cfg.html = this.html || cfg.html;
1478 cfg.src = this.src || cfg.src;
1480 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481 cfg.cls += ' img-' + this.border;
1498 a.target = this.target;
1503 return (this.href) ? a : cfg;
1506 initEvents: function()
1509 this.el.on('click', this.onClick, this);
1514 onClick : function(e)
1516 Roo.log('img onclick');
1517 this.fireEvent('click', this, e);
1520 * Sets the url of the image - used to update it
1521 * @param {String} url the url of the image
1524 setSrc : function(url)
1528 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529 this.el.dom.src = url;
1533 this.el.select('img', true).first().dom.src = url;
1549 * @class Roo.bootstrap.Link
1550 * @extends Roo.bootstrap.Component
1551 * Bootstrap Link Class
1552 * @cfg {String} alt image alternative text
1553 * @cfg {String} href a tag href
1554 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555 * @cfg {String} html the content of the link.
1556 * @cfg {String} anchor name for the anchor link
1557 * @cfg {String} fa - favicon
1559 * @cfg {Boolean} preventDefault (true | false) default false
1563 * Create a new Input
1564 * @param {Object} config The config object
1567 Roo.bootstrap.Link = function(config){
1568 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1574 * The img click event for the img.
1575 * @param {Roo.EventObject} e
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1585 preventDefault: false,
1591 getAutoCreate : function()
1593 var html = this.html || '';
1595 if (this.fa !== false) {
1596 html = '<i class="fa fa-' + this.fa + '"></i>';
1601 // anchor's do not require html/href...
1602 if (this.anchor === false) {
1604 cfg.href = this.href || '#';
1606 cfg.name = this.anchor;
1607 if (this.html !== false || this.fa !== false) {
1610 if (this.href !== false) {
1611 cfg.href = this.href;
1615 if(this.alt !== false){
1620 if(this.target !== false) {
1621 cfg.target = this.target;
1627 initEvents: function() {
1629 if(!this.href || this.preventDefault){
1630 this.el.on('click', this.onClick, this);
1634 onClick : function(e)
1636 if(this.preventDefault){
1639 //Roo.log('img onclick');
1640 this.fireEvent('click', this, e);
1653 * @class Roo.bootstrap.Header
1654 * @extends Roo.bootstrap.Component
1655 * Bootstrap Header class
1656 * @cfg {String} html content of header
1657 * @cfg {Number} level (1|2|3|4|5|6) default 1
1660 * Create a new Header
1661 * @param {Object} config The config object
1665 Roo.bootstrap.Header = function(config){
1666 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1677 getAutoCreate : function(){
1682 tag: 'h' + (1 *this.level),
1683 html: this.html || ''
1695 * Ext JS Library 1.1.1
1696 * Copyright(c) 2006-2007, Ext JS, LLC.
1698 * Originally Released Under LGPL - original licence link has changed is not relivant.
1701 * <script type="text/javascript">
1705 * @class Roo.bootstrap.MenuMgr
1706 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1709 Roo.bootstrap.MenuMgr = function(){
1710 var menus, active, groups = {}, attached = false, lastShow = new Date();
1712 // private - called when first menu is created
1715 active = new Roo.util.MixedCollection();
1716 Roo.get(document).addKeyListener(27, function(){
1717 if(active.length > 0){
1725 if(active && active.length > 0){
1726 var c = active.clone();
1736 if(active.length < 1){
1737 Roo.get(document).un("mouseup", onMouseDown);
1745 var last = active.last();
1746 lastShow = new Date();
1749 Roo.get(document).on("mouseup", onMouseDown);
1754 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755 m.parentMenu.activeChild = m;
1756 }else if(last && last.isVisible()){
1757 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1762 function onBeforeHide(m){
1764 m.activeChild.hide();
1766 if(m.autoHideTimer){
1767 clearTimeout(m.autoHideTimer);
1768 delete m.autoHideTimer;
1773 function onBeforeShow(m){
1774 var pm = m.parentMenu;
1775 if(!pm && !m.allowOtherMenus){
1777 }else if(pm && pm.activeChild && active != m){
1778 pm.activeChild.hide();
1782 // private this should really trigger on mouseup..
1783 function onMouseDown(e){
1784 Roo.log("on Mouse Up");
1786 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787 Roo.log("MenuManager hideAll");
1796 function onBeforeCheck(mi, state){
1798 var g = groups[mi.group];
1799 for(var i = 0, l = g.length; i < l; i++){
1801 g[i].setChecked(false);
1810 * Hides all menus that are currently visible
1812 hideAll : function(){
1817 register : function(menu){
1821 menus[menu.id] = menu;
1822 menu.on("beforehide", onBeforeHide);
1823 menu.on("hide", onHide);
1824 menu.on("beforeshow", onBeforeShow);
1825 menu.on("show", onShow);
1827 if(g && menu.events["checkchange"]){
1831 groups[g].push(menu);
1832 menu.on("checkchange", onCheck);
1837 * Returns a {@link Roo.menu.Menu} object
1838 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839 * be used to generate and return a new Menu instance.
1841 get : function(menu){
1842 if(typeof menu == "string"){ // menu id
1844 }else if(menu.events){ // menu instance
1847 /*else if(typeof menu.length == 'number'){ // array of menu items?
1848 return new Roo.bootstrap.Menu({items:menu});
1849 }else{ // otherwise, must be a config
1850 return new Roo.bootstrap.Menu(menu);
1857 unregister : function(menu){
1858 delete menus[menu.id];
1859 menu.un("beforehide", onBeforeHide);
1860 menu.un("hide", onHide);
1861 menu.un("beforeshow", onBeforeShow);
1862 menu.un("show", onShow);
1864 if(g && menu.events["checkchange"]){
1865 groups[g].remove(menu);
1866 menu.un("checkchange", onCheck);
1871 registerCheckable : function(menuItem){
1872 var g = menuItem.group;
1877 groups[g].push(menuItem);
1878 menuItem.on("beforecheckchange", onBeforeCheck);
1883 unregisterCheckable : function(menuItem){
1884 var g = menuItem.group;
1886 groups[g].remove(menuItem);
1887 menuItem.un("beforecheckchange", onBeforeCheck);
1899 * @class Roo.bootstrap.Menu
1900 * @extends Roo.bootstrap.Component
1901 * Bootstrap Menu class - container for MenuItems
1902 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903 * @cfg {bool} hidden if the menu should be hidden when rendered.
1904 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1905 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1909 * @param {Object} config The config object
1913 Roo.bootstrap.Menu = function(config){
1914 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915 if (this.registerMenu && this.type != 'treeview') {
1916 Roo.bootstrap.MenuMgr.register(this);
1921 * Fires before this menu is displayed
1922 * @param {Roo.menu.Menu} this
1927 * Fires before this menu is hidden
1928 * @param {Roo.menu.Menu} this
1933 * Fires after this menu is displayed
1934 * @param {Roo.menu.Menu} this
1939 * Fires after this menu is hidden
1940 * @param {Roo.menu.Menu} this
1945 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946 * @param {Roo.menu.Menu} this
1947 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * @param {Roo.EventObject} e
1953 * Fires when the mouse is hovering over this menu
1954 * @param {Roo.menu.Menu} this
1955 * @param {Roo.EventObject} e
1956 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1961 * Fires when the mouse exits this menu
1962 * @param {Roo.menu.Menu} this
1963 * @param {Roo.EventObject} e
1964 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969 * Fires when a menu item contained in this menu is clicked
1970 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971 * @param {Roo.EventObject} e
1975 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1982 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1985 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1987 registerMenu : true,
1989 menuItems :false, // stores the menu items..
1999 getChildContainer : function() {
2003 getAutoCreate : function(){
2005 //if (['right'].indexOf(this.align)!==-1) {
2006 // cfg.cn[1].cls += ' pull-right'
2012 cls : 'dropdown-menu' ,
2013 style : 'z-index:1000'
2017 if (this.type === 'submenu') {
2018 cfg.cls = 'submenu active';
2020 if (this.type === 'treeview') {
2021 cfg.cls = 'treeview-menu';
2026 initEvents : function() {
2028 // Roo.log("ADD event");
2029 // Roo.log(this.triggerEl.dom);
2031 this.triggerEl.on('click', this.onTriggerClick, this);
2033 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2035 this.triggerEl.addClass('dropdown-toggle');
2038 this.el.on('touchstart' , this.onTouch, this);
2040 this.el.on('click' , this.onClick, this);
2042 this.el.on("mouseover", this.onMouseOver, this);
2043 this.el.on("mouseout", this.onMouseOut, this);
2047 findTargetItem : function(e)
2049 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2053 //Roo.log(t); Roo.log(t.id);
2055 //Roo.log(this.menuitems);
2056 return this.menuitems.get(t.id);
2058 //return this.items.get(t.menuItemId);
2064 onTouch : function(e)
2066 Roo.log("menu.onTouch");
2067 //e.stopEvent(); this make the user popdown broken
2071 onClick : function(e)
2073 Roo.log("menu.onClick");
2075 var t = this.findTargetItem(e);
2076 if(!t || t.isContainer){
2081 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2082 if(t == this.activeItem && t.shouldDeactivate(e)){
2083 this.activeItem.deactivate();
2084 delete this.activeItem;
2088 this.setActiveItem(t, true);
2096 Roo.log('pass click event');
2100 this.fireEvent("click", this, t, e);
2104 (function() { _this.hide(); }).defer(100);
2107 onMouseOver : function(e){
2108 var t = this.findTargetItem(e);
2111 // if(t.canActivate && !t.disabled){
2112 // this.setActiveItem(t, true);
2116 this.fireEvent("mouseover", this, e, t);
2118 isVisible : function(){
2119 return !this.hidden;
2121 onMouseOut : function(e){
2122 var t = this.findTargetItem(e);
2125 // if(t == this.activeItem && t.shouldDeactivate(e)){
2126 // this.activeItem.deactivate();
2127 // delete this.activeItem;
2130 this.fireEvent("mouseout", this, e, t);
2135 * Displays this menu relative to another element
2136 * @param {String/HTMLElement/Roo.Element} element The element to align to
2137 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138 * the element (defaults to this.defaultAlign)
2139 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2141 show : function(el, pos, parentMenu){
2142 this.parentMenu = parentMenu;
2146 this.fireEvent("beforeshow", this);
2147 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2150 * Displays this menu at a specific xy position
2151 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2154 showAt : function(xy, parentMenu, /* private: */_e){
2155 this.parentMenu = parentMenu;
2160 this.fireEvent("beforeshow", this);
2161 //xy = this.el.adjustForConstraints(xy);
2165 this.hideMenuItems();
2166 this.hidden = false;
2167 this.triggerEl.addClass('open');
2169 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2173 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2178 this.fireEvent("show", this);
2184 this.doFocus.defer(50, this);
2188 doFocus : function(){
2190 this.focusEl.focus();
2195 * Hides this menu and optionally all parent menus
2196 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2198 hide : function(deep)
2201 this.hideMenuItems();
2202 if(this.el && this.isVisible()){
2203 this.fireEvent("beforehide", this);
2204 if(this.activeItem){
2205 this.activeItem.deactivate();
2206 this.activeItem = null;
2208 this.triggerEl.removeClass('open');;
2210 this.fireEvent("hide", this);
2212 if(deep === true && this.parentMenu){
2213 this.parentMenu.hide(true);
2217 onTriggerClick : function(e)
2219 Roo.log('trigger click');
2221 var target = e.getTarget();
2223 Roo.log(target.nodeName.toLowerCase());
2225 if(target.nodeName.toLowerCase() === 'i'){
2231 onTriggerPress : function(e)
2233 Roo.log('trigger press');
2234 //Roo.log(e.getTarget());
2235 // Roo.log(this.triggerEl.dom);
2237 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238 var pel = Roo.get(e.getTarget());
2239 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240 Roo.log('is treeview or dropdown?');
2244 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2248 if (this.isVisible()) {
2253 this.show(this.triggerEl, false, false);
2256 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2263 hideMenuItems : function()
2265 Roo.log("hide Menu Items");
2269 //$(backdrop).remove()
2270 this.el.select('.open',true).each(function(aa) {
2272 aa.removeClass('open');
2273 //var parent = getParent($(this))
2274 //var relatedTarget = { relatedTarget: this }
2276 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277 //if (e.isDefaultPrevented()) return
2278 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2281 addxtypeChild : function (tree, cntr) {
2282 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2284 this.menuitems.add(comp);
2305 * @class Roo.bootstrap.MenuItem
2306 * @extends Roo.bootstrap.Component
2307 * Bootstrap MenuItem class
2308 * @cfg {String} html the menu label
2309 * @cfg {String} href the link
2310 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312 * @cfg {Boolean} active used on sidebars to highlight active itesm
2313 * @cfg {String} fa favicon to show on left of menu item.
2314 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2318 * Create a new MenuItem
2319 * @param {Object} config The config object
2323 Roo.bootstrap.MenuItem = function(config){
2324 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2329 * The raw click event for the entire grid.
2330 * @param {Roo.bootstrap.MenuItem} this
2331 * @param {Roo.EventObject} e
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2341 preventDefault: false,
2342 isContainer : false,
2346 getAutoCreate : function(){
2348 if(this.isContainer){
2351 cls: 'dropdown-menu-item'
2365 if (this.fa !== false) {
2368 cls : 'fa fa-' + this.fa
2377 cls: 'dropdown-menu-item',
2380 if (this.parent().type == 'treeview') {
2381 cfg.cls = 'treeview-menu';
2384 cfg.cls += ' active';
2389 anc.href = this.href || cfg.cn[0].href ;
2390 ctag.html = this.html || cfg.cn[0].html ;
2394 initEvents: function()
2396 if (this.parent().type == 'treeview') {
2397 this.el.select('a').on('click', this.onClick, this);
2400 this.menu.parentType = this.xtype;
2401 this.menu.triggerEl = this.el;
2402 this.menu = this.addxtype(Roo.apply({}, this.menu));
2406 onClick : function(e)
2408 Roo.log('item on click ');
2410 if(this.preventDefault){
2413 //this.parent().hideMenuItems();
2415 this.fireEvent('click', this, e);
2434 * @class Roo.bootstrap.MenuSeparator
2435 * @extends Roo.bootstrap.Component
2436 * Bootstrap MenuSeparator class
2439 * Create a new MenuItem
2440 * @param {Object} config The config object
2444 Roo.bootstrap.MenuSeparator = function(config){
2445 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2450 getAutoCreate : function(){
2469 * @class Roo.bootstrap.Modal
2470 * @extends Roo.bootstrap.Component
2471 * Bootstrap Modal class
2472 * @cfg {String} title Title of dialog
2473 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2475 * @cfg {Boolean} specificTitle default false
2476 * @cfg {Array} buttons Array of buttons or standard button set..
2477 * @cfg {String} buttonPosition (left|right|center) default right
2478 * @cfg {Boolean} animate default true
2479 * @cfg {Boolean} allow_close default true
2480 * @cfg {Boolean} fitwindow default false
2481 * @cfg {String} size (sm|lg) default empty
2485 * Create a new Modal Dialog
2486 * @param {Object} config The config object
2489 Roo.bootstrap.Modal = function(config){
2490 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2495 * The raw btnclick event for the button
2496 * @param {Roo.EventObject} e
2500 this.buttons = this.buttons || [];
2503 this.tmpl = Roo.factory(this.tmpl);
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2510 title : 'test dialog',
2520 specificTitle: false,
2522 buttonPosition: 'right',
2541 onRender : function(ct, position)
2543 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2546 var cfg = Roo.apply({}, this.getAutoCreate());
2549 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2551 //if (!cfg.name.length) {
2555 cfg.cls += ' ' + this.cls;
2558 cfg.style = this.style;
2560 this.el = Roo.get(document.body).createChild(cfg, position);
2562 //var type = this.el.dom.type;
2565 if(this.tabIndex !== undefined){
2566 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2569 this.dialogEl = this.el.select('.modal-dialog',true).first();
2570 this.bodyEl = this.el.select('.modal-body',true).first();
2571 this.closeEl = this.el.select('.modal-header .close', true).first();
2572 this.headerEl = this.el.select('.modal-header',true).first();
2573 this.titleEl = this.el.select('.modal-title',true).first();
2574 this.footerEl = this.el.select('.modal-footer',true).first();
2576 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577 this.maskEl.enableDisplayMode("block");
2579 //this.el.addClass("x-dlg-modal");
2581 if (this.buttons.length) {
2582 Roo.each(this.buttons, function(bb) {
2583 var b = Roo.apply({}, bb);
2584 b.xns = b.xns || Roo.bootstrap;
2585 b.xtype = b.xtype || 'Button';
2586 if (typeof(b.listeners) == 'undefined') {
2587 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2590 var btn = Roo.factory(b);
2592 btn.render(this.el.select('.modal-footer div').first());
2596 // render the children.
2599 if(typeof(this.items) != 'undefined'){
2600 var items = this.items;
2603 for(var i =0;i < items.length;i++) {
2604 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2608 this.items = nitems;
2610 // where are these used - they used to be body/close/footer
2614 //this.el.addClass([this.fieldClass, this.cls]);
2618 getAutoCreate : function(){
2623 html : this.html || ''
2628 cls : 'modal-title',
2632 if(this.specificTitle){
2638 if (this.allow_close) {
2650 if(this.size.length){
2651 size = 'modal-' + this.size;
2656 style : 'display: none',
2659 cls: "modal-dialog " + size,
2662 cls : "modal-content",
2665 cls : 'modal-header',
2670 cls : 'modal-footer',
2674 cls: 'btn-' + this.buttonPosition
2691 modal.cls += ' fade';
2697 getChildContainer : function() {
2702 getButtonContainer : function() {
2703 return this.el.select('.modal-footer div',true).first();
2706 initEvents : function()
2708 if (this.allow_close) {
2709 this.closeEl.on('click', this.hide, this);
2711 Roo.EventManager.onWindowResize(this.resize, this, true);
2718 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2719 if (this.fitwindow) {
2720 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2726 setSize : function(w,h)
2736 if (!this.rendered) {
2740 this.el.setStyle('display', 'block');
2742 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2745 this.el.addClass('in');
2748 this.el.addClass('in');
2752 // not sure how we can show data in here..
2754 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2757 Roo.get(document.body).addClass("x-body-masked");
2759 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2760 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2765 this.fireEvent('show', this);
2767 // set zindex here - otherwise it appears to be ignored...
2768 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2771 this.items.forEach( function(e) {
2772 e.layout ? e.layout() : false;
2780 if(this.fireEvent("beforehide", this) !== false){
2782 Roo.get(document.body).removeClass("x-body-masked");
2783 this.el.removeClass('in');
2784 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2786 if(this.animate){ // why
2788 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2790 this.el.setStyle('display', 'none');
2792 this.fireEvent('hide', this);
2796 addButton : function(str, cb)
2800 var b = Roo.apply({}, { html : str } );
2801 b.xns = b.xns || Roo.bootstrap;
2802 b.xtype = b.xtype || 'Button';
2803 if (typeof(b.listeners) == 'undefined') {
2804 b.listeners = { click : cb.createDelegate(this) };
2807 var btn = Roo.factory(b);
2809 btn.render(this.el.select('.modal-footer div').first());
2815 setDefaultButton : function(btn)
2817 //this.el.select('.modal-footer').()
2821 resizeTo: function(w,h)
2825 this.dialogEl.setWidth(w);
2826 if (this.diff === false) {
2827 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2830 this.bodyEl.setHeight(h-this.diff);
2834 setContentSize : function(w, h)
2838 onButtonClick: function(btn,e)
2841 this.fireEvent('btnclick', btn.name, e);
2844 * Set the title of the Dialog
2845 * @param {String} str new Title
2847 setTitle: function(str) {
2848 this.titleEl.dom.innerHTML = str;
2851 * Set the body of the Dialog
2852 * @param {String} str new Title
2854 setBody: function(str) {
2855 this.bodyEl.dom.innerHTML = str;
2858 * Set the body of the Dialog using the template
2859 * @param {Obj} data - apply this data to the template and replace the body contents.
2861 applyBody: function(obj)
2864 Roo.log("Error - using apply Body without a template");
2867 this.tmpl.overwrite(this.bodyEl, obj);
2873 Roo.apply(Roo.bootstrap.Modal, {
2875 * Button config that displays a single OK button
2884 * Button config that displays Yes and No buttons
2900 * Button config that displays OK and Cancel buttons
2915 * Button config that displays Yes, No and Cancel buttons
2939 * messagebox - can be used as a replace
2943 * @class Roo.MessageBox
2944 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2953 // process text value...
2957 // Show a dialog using config options:
2959 title:'Save Changes?',
2960 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961 buttons: Roo.Msg.YESNOCANCEL,
2968 Roo.bootstrap.MessageBox = function(){
2969 var dlg, opt, mask, waitTimer;
2970 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971 var buttons, activeTextEl, bwidth;
2975 var handleButton = function(button){
2977 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2981 var handleHide = function(){
2983 dlg.el.removeClass(opt.cls);
2986 // Roo.TaskMgr.stop(waitTimer);
2987 // waitTimer = null;
2992 var updateButtons = function(b){
2995 buttons["ok"].hide();
2996 buttons["cancel"].hide();
2997 buttons["yes"].hide();
2998 buttons["no"].hide();
2999 //dlg.footer.dom.style.display = 'none';
3002 dlg.footerEl.dom.style.display = '';
3003 for(var k in buttons){
3004 if(typeof buttons[k] != "function"){
3007 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008 width += buttons[k].el.getWidth()+15;
3018 var handleEsc = function(d, k, e){
3019 if(opt && opt.closable !== false){
3029 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030 * @return {Roo.BasicDialog} The BasicDialog element
3032 getDialog : function(){
3034 dlg = new Roo.bootstrap.Modal( {
3037 //constraintoviewport:false,
3039 //collapsible : false,
3044 //buttonAlign:"center",
3045 closeClick : function(){
3046 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3049 handleButton("cancel");
3054 dlg.on("hide", handleHide);
3056 //dlg.addKeyListener(27, handleEsc);
3058 this.buttons = buttons;
3059 var bt = this.buttonText;
3060 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3065 bodyEl = dlg.bodyEl.createChild({
3067 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068 '<textarea class="roo-mb-textarea"></textarea>' +
3069 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3071 msgEl = bodyEl.dom.firstChild;
3072 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073 textboxEl.enableDisplayMode();
3074 textboxEl.addKeyListener([10,13], function(){
3075 if(dlg.isVisible() && opt && opt.buttons){
3078 }else if(opt.buttons.yes){
3079 handleButton("yes");
3083 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084 textareaEl.enableDisplayMode();
3085 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086 progressEl.enableDisplayMode();
3088 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089 //var pf = progressEl.dom.firstChild;
3091 //pp = Roo.get(pf.firstChild);
3092 //pp.setHeight(pf.offsetHeight);
3100 * Updates the message box body text
3101 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102 * the XHTML-compliant non-breaking space character '&#160;')
3103 * @return {Roo.MessageBox} This message box
3105 updateText : function(text)
3107 if(!dlg.isVisible() && !opt.width){
3108 dlg.dialogEl.setWidth(this.maxWidth);
3109 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3111 msgEl.innerHTML = text || ' ';
3113 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3116 Math.min(opt.width || cw , this.maxWidth),
3117 Math.max(opt.minWidth || this.minWidth, bwidth)
3120 activeTextEl.setWidth(w);
3122 if(dlg.isVisible()){
3123 dlg.fixedcenter = false;
3125 // to big, make it scroll. = But as usual stupid IE does not support
3128 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3132 bodyEl.dom.style.height = '';
3133 bodyEl.dom.style.overflowY = '';
3136 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3138 bodyEl.dom.style.overflowX = '';
3141 dlg.setContentSize(w, bodyEl.getHeight());
3142 if(dlg.isVisible()){
3143 dlg.fixedcenter = true;
3149 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3150 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153 * @return {Roo.MessageBox} This message box
3155 updateProgress : function(value, text){
3157 this.updateText(text);
3159 if (pp) { // weird bug on my firefox - for some reason this is not defined
3160 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3166 * Returns true if the message box is currently displayed
3167 * @return {Boolean} True if the message box is visible, else false
3169 isVisible : function(){
3170 return dlg && dlg.isVisible();
3174 * Hides the message box if it is displayed
3177 if(this.isVisible()){
3183 * Displays a new message box, or reinitializes an existing message box, based on the config options
3184 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185 * The following config object properties are supported:
3187 Property Type Description
3188 ---------- --------------- ------------------------------------------------------------------------------------
3189 animEl String/Element An id or Element from which the message box should animate as it opens and
3190 closes (defaults to undefined)
3191 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable Boolean False to hide the top-right close button (defaults to true). Note that
3194 progress and wait dialogs will ignore this property and always hide the
3195 close button as they can only be closed programmatically.
3196 cls String A custom CSS class to apply to the message box element
3197 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3198 displayed (defaults to 75)
3199 fn Function A callback function to execute after closing the dialog. The arguments to the
3200 function will be btn (the name of the button that was clicked, if applicable,
3201 e.g. "ok"), and text (the value of the active text field, if applicable).
3202 Progress and wait dialogs will ignore this option since they do not respond to
3203 user actions and can only be closed programmatically, so any required function
3204 should be called by the same code after it closes the dialog.
3205 icon String A CSS class that provides a background image to be used as an icon for
3206 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3208 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3209 modal Boolean False to allow user interaction with the page while the message box is
3210 displayed (defaults to true)
3211 msg String A string that will replace the existing message box body text (defaults
3212 to the XHTML-compliant non-breaking space character ' ')
3213 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3214 progress Boolean True to display a progress bar (defaults to false)
3215 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3218 title String The title text
3219 value String The string value to set into the active textbox element if displayed
3220 wait Boolean True to display a progress bar (defaults to false)
3221 width Number The width of the dialog in pixels
3228 msg: 'Please enter your address:',
3230 buttons: Roo.MessageBox.OKCANCEL,
3233 animEl: 'addAddressBtn'
3236 * @param {Object} config Configuration options
3237 * @return {Roo.MessageBox} This message box
3239 show : function(options)
3242 // this causes nightmares if you show one dialog after another
3243 // especially on callbacks..
3245 if(this.isVisible()){
3248 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3250 Roo.log("New Dialog Message:" + options.msg )
3251 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3255 var d = this.getDialog();
3257 d.setTitle(opt.title || " ");
3258 d.closeEl.setDisplayed(opt.closable !== false);
3259 activeTextEl = textboxEl;
3260 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3265 textareaEl.setHeight(typeof opt.multiline == "number" ?
3266 opt.multiline : this.defaultTextHeight);
3267 activeTextEl = textareaEl;
3276 progressEl.setDisplayed(opt.progress === true);
3277 this.updateProgress(0);
3278 activeTextEl.dom.value = opt.value || "";
3280 dlg.setDefaultButton(activeTextEl);
3282 var bs = opt.buttons;
3286 }else if(bs && bs.yes){
3287 db = buttons["yes"];
3289 dlg.setDefaultButton(db);
3291 bwidth = updateButtons(opt.buttons);
3292 this.updateText(opt.msg);
3294 d.el.addClass(opt.cls);
3296 d.proxyDrag = opt.proxyDrag === true;
3297 d.modal = opt.modal !== false;
3298 d.mask = opt.modal !== false ? mask : false;
3300 // force it to the end of the z-index stack so it gets a cursor in FF
3301 document.body.appendChild(dlg.el.dom);
3302 d.animateTarget = null;
3303 d.show(options.animEl);
3309 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3310 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311 * and closing the message box when the process is complete.
3312 * @param {String} title The title bar text
3313 * @param {String} msg The message box body text
3314 * @return {Roo.MessageBox} This message box
3316 progress : function(title, msg){
3323 minWidth: this.minProgressWidth,
3330 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331 * If a callback function is passed it will be called after the user clicks the button, and the
3332 * id of the button that was clicked will be passed as the only parameter to the callback
3333 * (could also be the top-right close button).
3334 * @param {String} title The title bar text
3335 * @param {String} msg The message box body text
3336 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337 * @param {Object} scope (optional) The scope of the callback function
3338 * @return {Roo.MessageBox} This message box
3340 alert : function(title, msg, fn, scope)
3355 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3356 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357 * You are responsible for closing the message box when the process is complete.
3358 * @param {String} msg The message box body text
3359 * @param {String} title (optional) The title bar text
3360 * @return {Roo.MessageBox} This message box
3362 wait : function(msg, title){
3373 waitTimer = Roo.TaskMgr.start({
3375 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3383 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386 * @param {String} title The title bar text
3387 * @param {String} msg The message box body text
3388 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389 * @param {Object} scope (optional) The scope of the callback function
3390 * @return {Roo.MessageBox} This message box
3392 confirm : function(title, msg, fn, scope){
3396 buttons: this.YESNO,
3405 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3407 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408 * (could also be the top-right close button) and the text that was entered will be passed as the two
3409 * parameters to the callback.
3410 * @param {String} title The title bar text
3411 * @param {String} msg The message box body text
3412 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413 * @param {Object} scope (optional) The scope of the callback function
3414 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416 * @return {Roo.MessageBox} This message box
3418 prompt : function(title, msg, fn, scope, multiline){
3422 buttons: this.OKCANCEL,
3427 multiline: multiline,
3434 * Button config that displays a single OK button
3439 * Button config that displays Yes and No buttons
3442 YESNO : {yes:true, no:true},
3444 * Button config that displays OK and Cancel buttons
3447 OKCANCEL : {ok:true, cancel:true},
3449 * Button config that displays Yes, No and Cancel buttons
3452 YESNOCANCEL : {yes:true, no:true, cancel:true},
3455 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3458 defaultTextHeight : 75,
3460 * The maximum width in pixels of the message box (defaults to 600)
3465 * The minimum width in pixels of the message box (defaults to 100)
3470 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3471 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3474 minProgressWidth : 250,
3476 * An object containing the default button text strings that can be overriden for localized language support.
3477 * Supported properties are: ok, cancel, yes and no.
3478 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3491 * Shorthand for {@link Roo.MessageBox}
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3503 * @class Roo.bootstrap.Navbar
3504 * @extends Roo.bootstrap.Component
3505 * Bootstrap Navbar class
3508 * Create a new Navbar
3509 * @param {Object} config The config object
3513 Roo.bootstrap.Navbar = function(config){
3514 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3518 * @event beforetoggle
3519 * Fire before toggle the menu
3520 * @param {Roo.EventObject} e
3522 "beforetoggle" : true
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3535 getAutoCreate : function(){
3538 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3542 initEvents :function ()
3544 //Roo.log(this.el.select('.navbar-toggle',true));
3545 this.el.select('.navbar-toggle',true).on('click', function() {
3546 if(this.fireEvent('beforetoggle', this) !== false){
3547 this.el.select('.navbar-collapse',true).toggleClass('in');
3557 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3559 var size = this.el.getSize();
3560 this.maskEl.setSize(size.width, size.height);
3561 this.maskEl.enableDisplayMode("block");
3570 getChildContainer : function()
3572 if (this.el.select('.collapse').getCount()) {
3573 return this.el.select('.collapse',true).first();
3606 * @class Roo.bootstrap.NavSimplebar
3607 * @extends Roo.bootstrap.Navbar
3608 * Bootstrap Sidebar class
3610 * @cfg {Boolean} inverse is inverted color
3612 * @cfg {String} type (nav | pills | tabs)
3613 * @cfg {Boolean} arrangement stacked | justified
3614 * @cfg {String} align (left | right) alignment
3616 * @cfg {Boolean} main (true|false) main nav bar? default false
3617 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3619 * @cfg {String} tag (header|footer|nav|div) default is nav
3625 * Create a new Sidebar
3626 * @param {Object} config The config object
3630 Roo.bootstrap.NavSimplebar = function(config){
3631 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3650 getAutoCreate : function(){
3654 tag : this.tag || 'div',
3667 this.type = this.type || 'nav';
3668 if (['tabs','pills'].indexOf(this.type)!==-1) {
3669 cfg.cn[0].cls += ' nav-' + this.type
3673 if (this.type!=='nav') {
3674 Roo.log('nav type must be nav/tabs/pills')
3676 cfg.cn[0].cls += ' navbar-nav'
3682 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683 cfg.cn[0].cls += ' nav-' + this.arrangement;
3687 if (this.align === 'right') {
3688 cfg.cn[0].cls += ' navbar-right';
3692 cfg.cls += ' navbar-inverse';
3719 * @class Roo.bootstrap.NavHeaderbar
3720 * @extends Roo.bootstrap.NavSimplebar
3721 * Bootstrap Sidebar class
3723 * @cfg {String} brand what is brand
3724 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725 * @cfg {String} brand_href href of the brand
3726 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3727 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3732 * Create a new Sidebar
3733 * @param {Object} config The config object
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3749 desktopCenter : false,
3752 getAutoCreate : function(){
3755 tag: this.nav || 'nav',
3762 if (this.desktopCenter) {
3763 cn.push({cls : 'container', cn : []});
3770 cls: 'navbar-header',
3775 cls: 'navbar-toggle',
3776 'data-toggle': 'collapse',
3781 html: 'Toggle navigation'
3803 cls: 'collapse navbar-collapse',
3807 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3809 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810 cfg.cls += ' navbar-' + this.position;
3812 // tag can override this..
3814 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3817 if (this.brand !== '') {
3820 href: this.brand_href ? this.brand_href : '#',
3821 cls: 'navbar-brand',
3829 cfg.cls += ' main-nav';
3837 getHeaderChildContainer : function()
3839 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840 return this.el.select('.navbar-header',true).first();
3843 return this.getChildContainer();
3847 initEvents : function()
3849 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3851 if (this.autohide) {
3856 Roo.get(document).on('scroll',function(e) {
3857 var ns = Roo.get(document).getScroll().top;
3858 var os = prevScroll;
3862 ft.removeClass('slideDown');
3863 ft.addClass('slideUp');
3866 ft.removeClass('slideUp');
3867 ft.addClass('slideDown');
3888 * @class Roo.bootstrap.NavSidebar
3889 * @extends Roo.bootstrap.Navbar
3890 * Bootstrap Sidebar class
3893 * Create a new Sidebar
3894 * @param {Object} config The config object
3898 Roo.bootstrap.NavSidebar = function(config){
3899 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3904 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3906 getAutoCreate : function(){
3911 cls: 'sidebar sidebar-nav'
3933 * @class Roo.bootstrap.NavGroup
3934 * @extends Roo.bootstrap.Component
3935 * Bootstrap NavGroup class
3936 * @cfg {String} align (left|right)
3937 * @cfg {Boolean} inverse
3938 * @cfg {String} type (nav|pills|tab) default nav
3939 * @cfg {String} navId - reference Id for navbar.
3943 * Create a new nav group
3944 * @param {Object} config The config object
3947 Roo.bootstrap.NavGroup = function(config){
3948 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3951 Roo.bootstrap.NavGroup.register(this);
3955 * Fires when the active item changes
3956 * @param {Roo.bootstrap.NavGroup} this
3957 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3976 getAutoCreate : function()
3978 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3985 if (['tabs','pills'].indexOf(this.type)!==-1) {
3986 cfg.cls += ' nav-' + this.type
3988 if (this.type!=='nav') {
3989 Roo.log('nav type must be nav/tabs/pills')
3991 cfg.cls += ' navbar-nav'
3994 if (this.parent().sidebar) {
3997 cls: 'dashboard-menu sidebar-menu'
4003 if (this.form === true) {
4009 if (this.align === 'right') {
4010 cfg.cls += ' navbar-right';
4012 cfg.cls += ' navbar-left';
4016 if (this.align === 'right') {
4017 cfg.cls += ' navbar-right';
4021 cfg.cls += ' navbar-inverse';
4029 * sets the active Navigation item
4030 * @param {Roo.bootstrap.NavItem} the new current navitem
4032 setActiveItem : function(item)
4035 Roo.each(this.navItems, function(v){
4040 v.setActive(false, true);
4047 item.setActive(true, true);
4048 this.fireEvent('changed', this, item, prev);
4053 * gets the active Navigation item
4054 * @return {Roo.bootstrap.NavItem} the current navitem
4056 getActive : function()
4060 Roo.each(this.navItems, function(v){
4071 indexOfNav : function()
4075 Roo.each(this.navItems, function(v,i){
4086 * adds a Navigation item
4087 * @param {Roo.bootstrap.NavItem} the navitem to add
4089 addItem : function(cfg)
4091 var cn = new Roo.bootstrap.NavItem(cfg);
4093 cn.parentId = this.id;
4094 cn.onRender(this.el, null);
4098 * register a Navigation item
4099 * @param {Roo.bootstrap.NavItem} the navitem to add
4101 register : function(item)
4103 this.navItems.push( item);
4104 item.navId = this.navId;
4109 * clear all the Navigation item
4112 clearAll : function()
4115 this.el.dom.innerHTML = '';
4118 getNavItem: function(tabId)
4121 Roo.each(this.navItems, function(e) {
4122 if (e.tabId == tabId) {
4132 setActiveNext : function()
4134 var i = this.indexOfNav(this.getActive());
4135 if (i > this.navItems.length) {
4138 this.setActiveItem(this.navItems[i+1]);
4140 setActivePrev : function()
4142 var i = this.indexOfNav(this.getActive());
4146 this.setActiveItem(this.navItems[i-1]);
4148 clearWasActive : function(except) {
4149 Roo.each(this.navItems, function(e) {
4150 if (e.tabId != except.tabId && e.was_active) {
4151 e.was_active = false;
4158 getWasActive : function ()
4161 Roo.each(this.navItems, function(e) {
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4180 * register a Navigation Group
4181 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4183 register : function(navgrp)
4185 this.groups[navgrp.navId] = navgrp;
4189 * fetch a Navigation Group based on the navigation ID
4190 * @param {string} the navgroup to add
4191 * @returns {Roo.bootstrap.NavGroup} the navgroup
4193 get: function(navId) {
4194 if (typeof(this.groups[navId]) == 'undefined') {
4196 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4198 return this.groups[navId] ;
4213 * @class Roo.bootstrap.NavItem
4214 * @extends Roo.bootstrap.Component
4215 * Bootstrap Navbar.NavItem class
4216 * @cfg {String} href link to
4217 * @cfg {String} html content of button
4218 * @cfg {String} badge text inside badge
4219 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220 * @cfg {String} glyphicon name of glyphicon
4221 * @cfg {String} icon name of font awesome icon
4222 * @cfg {Boolean} active Is item active
4223 * @cfg {Boolean} disabled Is item disabled
4225 * @cfg {Boolean} preventDefault (true | false) default false
4226 * @cfg {String} tabId the tab that this item activates.
4227 * @cfg {String} tagtype (a|span) render as a href or span?
4228 * @cfg {Boolean} animateRef (true|false) link to element default false
4231 * Create a new Navbar Item
4232 * @param {Object} config The config object
4234 Roo.bootstrap.NavItem = function(config){
4235 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4240 * The raw click event for the entire grid.
4241 * @param {Roo.EventObject} e
4246 * Fires when the active item active state changes
4247 * @param {Roo.bootstrap.NavItem} this
4248 * @param {boolean} state the new state
4254 * Fires when scroll to element
4255 * @param {Roo.bootstrap.NavItem} this
4256 * @param {Object} options
4257 * @param {Roo.EventObject} e
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4273 preventDefault : false,
4280 getAutoCreate : function(){
4289 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4291 if (this.disabled) {
4292 cfg.cls += ' disabled';
4295 if (this.href || this.html || this.glyphicon || this.icon) {
4299 href : this.href || "#",
4300 html: this.html || ''
4305 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4308 if(this.glyphicon) {
4309 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4314 cfg.cn[0].html += " <span class='caret'></span>";
4318 if (this.badge !== '') {
4320 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4328 initEvents: function()
4330 if (typeof (this.menu) != 'undefined') {
4331 this.menu.parentType = this.xtype;
4332 this.menu.triggerEl = this.el;
4333 this.menu = this.addxtype(Roo.apply({}, this.menu));
4336 this.el.select('a',true).on('click', this.onClick, this);
4338 if(this.tagtype == 'span'){
4339 this.el.select('span',true).on('click', this.onClick, this);
4342 // at this point parent should be available..
4343 this.parent().register(this);
4346 onClick : function(e)
4348 if (e.getTarget('.dropdown-menu-item')) {
4349 // did you click on a menu itemm.... - then don't trigger onclick..
4354 this.preventDefault ||
4357 Roo.log("NavItem - prevent Default?");
4361 if (this.disabled) {
4365 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366 if (tg && tg.transition) {
4367 Roo.log("waiting for the transitionend");
4373 //Roo.log("fire event clicked");
4374 if(this.fireEvent('click', this, e) === false){
4378 if(this.tagtype == 'span'){
4382 //Roo.log(this.href);
4383 var ael = this.el.select('a',true).first();
4386 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389 return; // ignore... - it's a 'hash' to another page.
4391 Roo.log("NavItem - prevent Default?");
4393 this.scrollToElement(e);
4397 var p = this.parent();
4399 if (['tabs','pills'].indexOf(p.type)!==-1) {
4400 if (typeof(p.setActiveItem) !== 'undefined') {
4401 p.setActiveItem(this);
4405 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407 // remove the collapsed menu expand...
4408 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4412 isActive: function () {
4415 setActive : function(state, fire, is_was_active)
4417 if (this.active && !state && this.navId) {
4418 this.was_active = true;
4419 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4421 nv.clearWasActive(this);
4425 this.active = state;
4428 this.el.removeClass('active');
4429 } else if (!this.el.hasClass('active')) {
4430 this.el.addClass('active');
4433 this.fireEvent('changed', this, state);
4436 // show a panel if it's registered and related..
4438 if (!this.navId || !this.tabId || !state || is_was_active) {
4442 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4446 var pan = tg.getPanelByName(this.tabId);
4450 // if we can not flip to new panel - go back to old nav highlight..
4451 if (false == tg.showPanel(pan)) {
4452 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454 var onav = nv.getWasActive();
4456 onav.setActive(true, false, true);
4465 // this should not be here...
4466 setDisabled : function(state)
4468 this.disabled = state;
4470 this.el.removeClass('disabled');
4471 } else if (!this.el.hasClass('disabled')) {
4472 this.el.addClass('disabled');
4478 * Fetch the element to display the tooltip on.
4479 * @return {Roo.Element} defaults to this.el
4481 tooltipEl : function()
4483 return this.el.select('' + this.tagtype + '', true).first();
4486 scrollToElement : function(e)
4488 var c = document.body;
4491 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4493 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494 c = document.documentElement;
4497 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4503 var o = target.calcOffsetsTo(c);
4510 this.fireEvent('scrollto', this, options, e);
4512 Roo.get(c).scrollTo('top', options.value, true);
4525 * <span> icon </span>
4526 * <span> text </span>
4527 * <span>badge </span>
4531 * @class Roo.bootstrap.NavSidebarItem
4532 * @extends Roo.bootstrap.NavItem
4533 * Bootstrap Navbar.NavSidebarItem class
4534 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535 * {bool} open is the menu open
4537 * Create a new Navbar Button
4538 * @param {Object} config The config object
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4546 * The raw click event for the entire grid.
4547 * @param {Roo.EventObject} e
4552 * Fires when the active item active state changes
4553 * @param {Roo.bootstrap.NavSidebarItem} this
4554 * @param {boolean} state the new state
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4564 badgeWeight : 'default',
4568 getAutoCreate : function(){
4573 href : this.href || '#',
4585 html : this.html || ''
4590 cfg.cls += ' active';
4593 if (this.disabled) {
4594 cfg.cls += ' disabled';
4597 cfg.cls += ' open x-open';
4600 if (this.glyphicon || this.icon) {
4601 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4602 a.cn.push({ tag : 'i', cls : c }) ;
4607 if (this.badge !== '') {
4609 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4613 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614 a.cls += 'dropdown-toggle treeview' ;
4622 initEvents : function()
4624 if (typeof (this.menu) != 'undefined') {
4625 this.menu.parentType = this.xtype;
4626 this.menu.triggerEl = this.el;
4627 this.menu = this.addxtype(Roo.apply({}, this.menu));
4630 this.el.on('click', this.onClick, this);
4633 if(this.badge !== ''){
4635 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4640 onClick : function(e)
4647 if(this.preventDefault){
4651 this.fireEvent('click', this);
4654 disable : function()
4656 this.setDisabled(true);
4661 this.setDisabled(false);
4664 setDisabled : function(state)
4666 if(this.disabled == state){
4670 this.disabled = state;
4673 this.el.addClass('disabled');
4677 this.el.removeClass('disabled');
4682 setActive : function(state)
4684 if(this.active == state){
4688 this.active = state;
4691 this.el.addClass('active');
4695 this.el.removeClass('active');
4700 isActive: function ()
4705 setBadge : function(str)
4711 this.badgeEl.dom.innerHTML = str;
4728 * @class Roo.bootstrap.Row
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Row class (contains columns...)
4734 * @param {Object} config The config object
4737 Roo.bootstrap.Row = function(config){
4738 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4743 getAutoCreate : function(){
4762 * @class Roo.bootstrap.Element
4763 * @extends Roo.bootstrap.Component
4764 * Bootstrap Element class
4765 * @cfg {String} html contents of the element
4766 * @cfg {String} tag tag of the element
4767 * @cfg {String} cls class of the element
4768 * @cfg {Boolean} preventDefault (true|false) default false
4769 * @cfg {Boolean} clickable (true|false) default false
4772 * Create a new Element
4773 * @param {Object} config The config object
4776 Roo.bootstrap.Element = function(config){
4777 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4783 * When a element is chick
4784 * @param {Roo.bootstrap.Element} this
4785 * @param {Roo.EventObject} e
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4796 preventDefault: false,
4799 getAutoCreate : function(){
4810 initEvents: function()
4812 Roo.bootstrap.Element.superclass.initEvents.call(this);
4815 this.el.on('click', this.onClick, this);
4820 onClick : function(e)
4822 if(this.preventDefault){
4826 this.fireEvent('click', this, e);
4829 getValue : function()
4831 return this.el.dom.innerHTML;
4834 setValue : function(value)
4836 this.el.dom.innerHTML = value;
4851 * @class Roo.bootstrap.Pagination
4852 * @extends Roo.bootstrap.Component
4853 * Bootstrap Pagination class
4854 * @cfg {String} size xs | sm | md | lg
4855 * @cfg {Boolean} inverse false | true
4858 * Create a new Pagination
4859 * @param {Object} config The config object
4862 Roo.bootstrap.Pagination = function(config){
4863 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4872 getAutoCreate : function(){
4878 cfg.cls += ' inverse';
4884 cfg.cls += " " + this.cls;
4902 * @class Roo.bootstrap.PaginationItem
4903 * @extends Roo.bootstrap.Component
4904 * Bootstrap PaginationItem class
4905 * @cfg {String} html text
4906 * @cfg {String} href the link
4907 * @cfg {Boolean} preventDefault (true | false) default true
4908 * @cfg {Boolean} active (true | false) default false
4909 * @cfg {Boolean} disabled default false
4913 * Create a new PaginationItem
4914 * @param {Object} config The config object
4918 Roo.bootstrap.PaginationItem = function(config){
4919 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4924 * The raw click event for the entire grid.
4925 * @param {Roo.EventObject} e
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4935 preventDefault: true,
4940 getAutoCreate : function(){
4946 href : this.href ? this.href : '#',
4947 html : this.html ? this.html : ''
4957 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4961 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4967 initEvents: function() {
4969 this.el.on('click', this.onClick, this);
4972 onClick : function(e)
4974 Roo.log('PaginationItem on click ');
4975 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4999 * @class Roo.bootstrap.Slider
5000 * @extends Roo.bootstrap.Component
5001 * Bootstrap Slider class
5004 * Create a new Slider
5005 * @param {Object} config The config object
5008 Roo.bootstrap.Slider = function(config){
5009 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5014 getAutoCreate : function(){
5018 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5022 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5034 * Ext JS Library 1.1.1
5035 * Copyright(c) 2006-2007, Ext JS, LLC.
5037 * Originally Released Under LGPL - original licence link has changed is not relivant.
5040 * <script type="text/javascript">
5045 * @class Roo.grid.ColumnModel
5046 * @extends Roo.util.Observable
5047 * This is the default implementation of a ColumnModel used by the Grid. It defines
5048 * the columns in the grid.
5051 var colModel = new Roo.grid.ColumnModel([
5052 {header: "Ticker", width: 60, sortable: true, locked: true},
5053 {header: "Company Name", width: 150, sortable: true},
5054 {header: "Market Cap.", width: 100, sortable: true},
5055 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056 {header: "Employees", width: 100, sortable: true, resizable: false}
5061 * The config options listed for this class are options which may appear in each
5062 * individual column definition.
5063 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5065 * @param {Object} config An Array of column config objects. See this class's
5066 * config objects for details.
5068 Roo.grid.ColumnModel = function(config){
5070 * The config passed into the constructor
5072 this.config = config;
5075 // if no id, create one
5076 // if the column does not have a dataIndex mapping,
5077 // map it to the order it is in the config
5078 for(var i = 0, len = config.length; i < len; i++){
5080 if(typeof c.dataIndex == "undefined"){
5083 if(typeof c.renderer == "string"){
5084 c.renderer = Roo.util.Format[c.renderer];
5086 if(typeof c.id == "undefined"){
5089 if(c.editor && c.editor.xtype){
5090 c.editor = Roo.factory(c.editor, Roo.grid);
5092 if(c.editor && c.editor.isFormField){
5093 c.editor = new Roo.grid.GridEditor(c.editor);
5095 this.lookup[c.id] = c;
5099 * The width of columns which have no width specified (defaults to 100)
5102 this.defaultWidth = 100;
5105 * Default sortable of columns which have no sortable specified (defaults to false)
5108 this.defaultSortable = false;
5112 * @event widthchange
5113 * Fires when the width of a column changes.
5114 * @param {ColumnModel} this
5115 * @param {Number} columnIndex The column index
5116 * @param {Number} newWidth The new width
5118 "widthchange": true,
5120 * @event headerchange
5121 * Fires when the text of a header changes.
5122 * @param {ColumnModel} this
5123 * @param {Number} columnIndex The column index
5124 * @param {Number} newText The new header text
5126 "headerchange": true,
5128 * @event hiddenchange
5129 * Fires when a column is hidden or "unhidden".
5130 * @param {ColumnModel} this
5131 * @param {Number} columnIndex The column index
5132 * @param {Boolean} hidden true if hidden, false otherwise
5134 "hiddenchange": true,
5136 * @event columnmoved
5137 * Fires when a column is moved.
5138 * @param {ColumnModel} this
5139 * @param {Number} oldIndex
5140 * @param {Number} newIndex
5142 "columnmoved" : true,
5144 * @event columlockchange
5145 * Fires when a column's locked state is changed
5146 * @param {ColumnModel} this
5147 * @param {Number} colIndex
5148 * @param {Boolean} locked true if locked
5150 "columnlockchange" : true
5152 Roo.grid.ColumnModel.superclass.constructor.call(this);
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5156 * @cfg {String} header The header text to display in the Grid view.
5159 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161 * specified, the column's index is used as an index into the Record's data Array.
5164 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5168 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169 * Defaults to the value of the {@link #defaultSortable} property.
5170 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5173 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5176 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5179 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5182 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5185 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5191 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5194 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5197 * @cfg {String} cursor (Optional)
5200 * @cfg {String} tooltip (Optional)
5203 * @cfg {Number} xs (Optional)
5206 * @cfg {Number} sm (Optional)
5209 * @cfg {Number} md (Optional)
5212 * @cfg {Number} lg (Optional)
5215 * Returns the id of the column at the specified index.
5216 * @param {Number} index The column index
5217 * @return {String} the id
5219 getColumnId : function(index){
5220 return this.config[index].id;
5224 * Returns the column for a specified id.
5225 * @param {String} id The column id
5226 * @return {Object} the column
5228 getColumnById : function(id){
5229 return this.lookup[id];
5234 * Returns the column for a specified dataIndex.
5235 * @param {String} dataIndex The column dataIndex
5236 * @return {Object|Boolean} the column or false if not found
5238 getColumnByDataIndex: function(dataIndex){
5239 var index = this.findColumnIndex(dataIndex);
5240 return index > -1 ? this.config[index] : false;
5244 * Returns the index for a specified column id.
5245 * @param {String} id The column id
5246 * @return {Number} the index, or -1 if not found
5248 getIndexById : function(id){
5249 for(var i = 0, len = this.config.length; i < len; i++){
5250 if(this.config[i].id == id){
5258 * Returns the index for a specified column dataIndex.
5259 * @param {String} dataIndex The column dataIndex
5260 * @return {Number} the index, or -1 if not found
5263 findColumnIndex : function(dataIndex){
5264 for(var i = 0, len = this.config.length; i < len; i++){
5265 if(this.config[i].dataIndex == dataIndex){
5273 moveColumn : function(oldIndex, newIndex){
5274 var c = this.config[oldIndex];
5275 this.config.splice(oldIndex, 1);
5276 this.config.splice(newIndex, 0, c);
5277 this.dataMap = null;
5278 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5281 isLocked : function(colIndex){
5282 return this.config[colIndex].locked === true;
5285 setLocked : function(colIndex, value, suppressEvent){
5286 if(this.isLocked(colIndex) == value){
5289 this.config[colIndex].locked = value;
5291 this.fireEvent("columnlockchange", this, colIndex, value);
5295 getTotalLockedWidth : function(){
5297 for(var i = 0; i < this.config.length; i++){
5298 if(this.isLocked(i) && !this.isHidden(i)){
5299 this.totalWidth += this.getColumnWidth(i);
5305 getLockedCount : function(){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 if(!this.isLocked(i)){
5312 return this.config.length;
5316 * Returns the number of columns.
5319 getColumnCount : function(visibleOnly){
5320 if(visibleOnly === true){
5322 for(var i = 0, len = this.config.length; i < len; i++){
5323 if(!this.isHidden(i)){
5329 return this.config.length;
5333 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334 * @param {Function} fn
5335 * @param {Object} scope (optional)
5336 * @return {Array} result
5338 getColumnsBy : function(fn, scope){
5340 for(var i = 0, len = this.config.length; i < len; i++){
5341 var c = this.config[i];
5342 if(fn.call(scope||this, c, i) === true){
5350 * Returns true if the specified column is sortable.
5351 * @param {Number} col The column index
5354 isSortable : function(col){
5355 if(typeof this.config[col].sortable == "undefined"){
5356 return this.defaultSortable;
5358 return this.config[col].sortable;
5362 * Returns the rendering (formatting) function defined for the column.
5363 * @param {Number} col The column index.
5364 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5366 getRenderer : function(col){
5367 if(!this.config[col].renderer){
5368 return Roo.grid.ColumnModel.defaultRenderer;
5370 return this.config[col].renderer;
5374 * Sets the rendering (formatting) function for a column.
5375 * @param {Number} col The column index
5376 * @param {Function} fn The function to use to process the cell's raw data
5377 * to return HTML markup for the grid view. The render function is called with
5378 * the following parameters:<ul>
5379 * <li>Data value.</li>
5380 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381 * <li>css A CSS style string to apply to the table cell.</li>
5382 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384 * <li>Row index</li>
5385 * <li>Column index</li>
5386 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5388 setRenderer : function(col, fn){
5389 this.config[col].renderer = fn;
5393 * Returns the width for the specified column.
5394 * @param {Number} col The column index
5397 getColumnWidth : function(col){
5398 return this.config[col].width * 1 || this.defaultWidth;
5402 * Sets the width for a column.
5403 * @param {Number} col The column index
5404 * @param {Number} width The new width
5406 setColumnWidth : function(col, width, suppressEvent){
5407 this.config[col].width = width;
5408 this.totalWidth = null;
5410 this.fireEvent("widthchange", this, col, width);
5415 * Returns the total width of all columns.
5416 * @param {Boolean} includeHidden True to include hidden column widths
5419 getTotalWidth : function(includeHidden){
5420 if(!this.totalWidth){
5421 this.totalWidth = 0;
5422 for(var i = 0, len = this.config.length; i < len; i++){
5423 if(includeHidden || !this.isHidden(i)){
5424 this.totalWidth += this.getColumnWidth(i);
5428 return this.totalWidth;
5432 * Returns the header for the specified column.
5433 * @param {Number} col The column index
5436 getColumnHeader : function(col){
5437 return this.config[col].header;
5441 * Sets the header for a column.
5442 * @param {Number} col The column index
5443 * @param {String} header The new header
5445 setColumnHeader : function(col, header){
5446 this.config[col].header = header;
5447 this.fireEvent("headerchange", this, col, header);
5451 * Returns the tooltip for the specified column.
5452 * @param {Number} col The column index
5455 getColumnTooltip : function(col){
5456 return this.config[col].tooltip;
5459 * Sets the tooltip for a column.
5460 * @param {Number} col The column index
5461 * @param {String} tooltip The new tooltip
5463 setColumnTooltip : function(col, tooltip){
5464 this.config[col].tooltip = tooltip;
5468 * Returns the dataIndex for the specified column.
5469 * @param {Number} col The column index
5472 getDataIndex : function(col){
5473 return this.config[col].dataIndex;
5477 * Sets the dataIndex for a column.
5478 * @param {Number} col The column index
5479 * @param {Number} dataIndex The new dataIndex
5481 setDataIndex : function(col, dataIndex){
5482 this.config[col].dataIndex = dataIndex;
5488 * Returns true if the cell is editable.
5489 * @param {Number} colIndex The column index
5490 * @param {Number} rowIndex The row index - this is nto actually used..?
5493 isCellEditable : function(colIndex, rowIndex){
5494 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5498 * Returns the editor defined for the cell/column.
5499 * return false or null to disable editing.
5500 * @param {Number} colIndex The column index
5501 * @param {Number} rowIndex The row index
5504 getCellEditor : function(colIndex, rowIndex){
5505 return this.config[colIndex].editor;
5509 * Sets if a column is editable.
5510 * @param {Number} col The column index
5511 * @param {Boolean} editable True if the column is editable
5513 setEditable : function(col, editable){
5514 this.config[col].editable = editable;
5519 * Returns true if the column is hidden.
5520 * @param {Number} colIndex The column index
5523 isHidden : function(colIndex){
5524 return this.config[colIndex].hidden;
5529 * Returns true if the column width cannot be changed
5531 isFixed : function(colIndex){
5532 return this.config[colIndex].fixed;
5536 * Returns true if the column can be resized
5539 isResizable : function(colIndex){
5540 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5543 * Sets if a column is hidden.
5544 * @param {Number} colIndex The column index
5545 * @param {Boolean} hidden True if the column is hidden
5547 setHidden : function(colIndex, hidden){
5548 this.config[colIndex].hidden = hidden;
5549 this.totalWidth = null;
5550 this.fireEvent("hiddenchange", this, colIndex, hidden);
5554 * Sets the editor for a column.
5555 * @param {Number} col The column index
5556 * @param {Object} editor The editor object
5558 setEditor : function(col, editor){
5559 this.config[col].editor = editor;
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5565 if(typeof value == "object") {
5568 if(typeof value == "string" && value.length < 1){
5572 return String.format("{0}", value);
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5579 * Ext JS Library 1.1.1
5580 * Copyright(c) 2006-2007, Ext JS, LLC.
5582 * Originally Released Under LGPL - original licence link has changed is not relivant.
5585 * <script type="text/javascript">
5589 * @class Roo.LoadMask
5590 * A simple utility class for generically masking elements while loading data. If the element being masked has
5591 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5593 * element's UpdateManager load indicator and will be destroyed after the initial load.
5595 * Create a new LoadMask
5596 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597 * @param {Object} config The config object
5599 Roo.LoadMask = function(el, config){
5600 this.el = Roo.get(el);
5601 Roo.apply(this, config);
5603 this.store.on('beforeload', this.onBeforeLoad, this);
5604 this.store.on('load', this.onLoad, this);
5605 this.store.on('loadexception', this.onLoadException, this);
5606 this.removeMask = false;
5608 var um = this.el.getUpdateManager();
5609 um.showLoadIndicator = false; // disable the default indicator
5610 um.on('beforeupdate', this.onBeforeLoad, this);
5611 um.on('update', this.onLoad, this);
5612 um.on('failure', this.onLoad, this);
5613 this.removeMask = true;
5617 Roo.LoadMask.prototype = {
5619 * @cfg {Boolean} removeMask
5620 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5625 * The text to display in a centered loading message box (defaults to 'Loading...')
5629 * @cfg {String} msgCls
5630 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5632 msgCls : 'x-mask-loading',
5635 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5641 * Disables the mask to prevent it from being displayed
5643 disable : function(){
5644 this.disabled = true;
5648 * Enables the mask so that it can be displayed
5650 enable : function(){
5651 this.disabled = false;
5654 onLoadException : function()
5658 if (typeof(arguments[3]) != 'undefined') {
5659 Roo.MessageBox.alert("Error loading",arguments[3]);
5663 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5673 this.el.unmask(this.removeMask);
5678 this.el.unmask(this.removeMask);
5682 onBeforeLoad : function(){
5684 this.el.mask(this.msg, this.msgCls);
5689 destroy : function(){
5691 this.store.un('beforeload', this.onBeforeLoad, this);
5692 this.store.un('load', this.onLoad, this);
5693 this.store.un('loadexception', this.onLoadException, this);
5695 var um = this.el.getUpdateManager();
5696 um.un('beforeupdate', this.onBeforeLoad, this);
5697 um.un('update', this.onLoad, this);
5698 um.un('failure', this.onLoad, this);
5709 * @class Roo.bootstrap.Table
5710 * @extends Roo.bootstrap.Component
5711 * Bootstrap Table class
5712 * @cfg {String} cls table class
5713 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714 * @cfg {String} bgcolor Specifies the background color for a table
5715 * @cfg {Number} border Specifies whether the table cells should have borders or not
5716 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717 * @cfg {Number} cellspacing Specifies the space between cells
5718 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720 * @cfg {String} sortable Specifies that the table should be sortable
5721 * @cfg {String} summary Specifies a summary of the content of a table
5722 * @cfg {Number} width Specifies the width of a table
5723 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5725 * @cfg {boolean} striped Should the rows be alternative striped
5726 * @cfg {boolean} bordered Add borders to the table
5727 * @cfg {boolean} hover Add hover highlighting
5728 * @cfg {boolean} condensed Format condensed
5729 * @cfg {boolean} responsive Format condensed
5730 * @cfg {Boolean} loadMask (true|false) default false
5731 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733 * @cfg {Boolean} rowSelection (true|false) default false
5734 * @cfg {Boolean} cellSelection (true|false) default false
5735 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5740 * Create a new Table
5741 * @param {Object} config The config object
5744 Roo.bootstrap.Table = function(config){
5745 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5750 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5755 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5757 this.sm.grid = this;
5758 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759 this.sm = this.selModel;
5760 this.sm.xmodule = this.xmodule || false;
5763 if (this.cm && typeof(this.cm.config) == 'undefined') {
5764 this.colModel = new Roo.grid.ColumnModel(this.cm);
5765 this.cm = this.colModel;
5766 this.cm.xmodule = this.xmodule || false;
5769 this.store= Roo.factory(this.store, Roo.data);
5770 this.ds = this.store;
5771 this.ds.xmodule = this.xmodule || false;
5774 if (this.footer && this.store) {
5775 this.footer.dataSource = this.ds;
5776 this.footer = Roo.factory(this.footer);
5783 * Fires when a cell is clicked
5784 * @param {Roo.bootstrap.Table} this
5785 * @param {Roo.Element} el
5786 * @param {Number} rowIndex
5787 * @param {Number} columnIndex
5788 * @param {Roo.EventObject} e
5792 * @event celldblclick
5793 * Fires when a cell is double clicked
5794 * @param {Roo.bootstrap.Table} this
5795 * @param {Roo.Element} el
5796 * @param {Number} rowIndex
5797 * @param {Number} columnIndex
5798 * @param {Roo.EventObject} e
5800 "celldblclick" : true,
5803 * Fires when a row is clicked
5804 * @param {Roo.bootstrap.Table} this
5805 * @param {Roo.Element} el
5806 * @param {Number} rowIndex
5807 * @param {Roo.EventObject} e
5811 * @event rowdblclick
5812 * Fires when a row is double clicked
5813 * @param {Roo.bootstrap.Table} this
5814 * @param {Roo.Element} el
5815 * @param {Number} rowIndex
5816 * @param {Roo.EventObject} e
5818 "rowdblclick" : true,
5821 * Fires when a mouseover occur
5822 * @param {Roo.bootstrap.Table} this
5823 * @param {Roo.Element} el
5824 * @param {Number} rowIndex
5825 * @param {Number} columnIndex
5826 * @param {Roo.EventObject} e
5831 * Fires when a mouseout occur
5832 * @param {Roo.bootstrap.Table} this
5833 * @param {Roo.Element} el
5834 * @param {Number} rowIndex
5835 * @param {Number} columnIndex
5836 * @param {Roo.EventObject} e
5841 * Fires when a row is rendered, so you can change add a style to it.
5842 * @param {Roo.bootstrap.Table} this
5843 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5847 * @event rowsrendered
5848 * Fires when all the rows have been rendered
5849 * @param {Roo.bootstrap.Table} this
5851 'rowsrendered' : true,
5853 * @event contextmenu
5854 * The raw contextmenu event for the entire grid.
5855 * @param {Roo.EventObject} e
5857 "contextmenu" : true,
5859 * @event rowcontextmenu
5860 * Fires when a row is right clicked
5861 * @param {Roo.bootstrap.Table} this
5862 * @param {Number} rowIndex
5863 * @param {Roo.EventObject} e
5865 "rowcontextmenu" : true,
5867 * @event cellcontextmenu
5868 * Fires when a cell is right clicked
5869 * @param {Roo.bootstrap.Table} this
5870 * @param {Number} rowIndex
5871 * @param {Number} cellIndex
5872 * @param {Roo.EventObject} e
5874 "cellcontextmenu" : true,
5876 * @event headercontextmenu
5877 * Fires when a header is right clicked
5878 * @param {Roo.bootstrap.Table} this
5879 * @param {Number} columnIndex
5880 * @param {Roo.EventObject} e
5882 "headercontextmenu" : true
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5912 rowSelection : false,
5913 cellSelection : false,
5916 // Roo.Element - the tbody
5918 // Roo.Element - thead element
5921 container: false, // used by gridpanel...
5923 getAutoCreate : function()
5925 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5932 if (this.scrollBody) {
5933 cfg.cls += ' table-body-fixed';
5936 cfg.cls += ' table-striped';
5940 cfg.cls += ' table-hover';
5942 if (this.bordered) {
5943 cfg.cls += ' table-bordered';
5945 if (this.condensed) {
5946 cfg.cls += ' table-condensed';
5948 if (this.responsive) {
5949 cfg.cls += ' table-responsive';
5953 cfg.cls+= ' ' +this.cls;
5956 // this lot should be simplifed...
5959 cfg.align=this.align;
5962 cfg.bgcolor=this.bgcolor;
5965 cfg.border=this.border;
5967 if (this.cellpadding) {
5968 cfg.cellpadding=this.cellpadding;
5970 if (this.cellspacing) {
5971 cfg.cellspacing=this.cellspacing;
5974 cfg.frame=this.frame;
5977 cfg.rules=this.rules;
5979 if (this.sortable) {
5980 cfg.sortable=this.sortable;
5983 cfg.summary=this.summary;
5986 cfg.width=this.width;
5989 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5992 if(this.store || this.cm){
5993 if(this.headerShow){
5994 cfg.cn.push(this.renderHeader());
5997 cfg.cn.push(this.renderBody());
5999 if(this.footerShow){
6000 cfg.cn.push(this.renderFooter());
6002 // where does this come from?
6003 //cfg.cls+= ' TableGrid';
6006 return { cn : [ cfg ] };
6009 initEvents : function()
6011 if(!this.store || !this.cm){
6014 if (this.selModel) {
6015 this.selModel.initEvents();
6019 //Roo.log('initEvents with ds!!!!');
6021 this.mainBody = this.el.select('tbody', true).first();
6022 this.mainHead = this.el.select('thead', true).first();
6029 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030 e.on('click', _this.sort, _this);
6033 this.mainBody.on("click", this.onClick, this);
6034 this.mainBody.on("dblclick", this.onDblClick, this);
6036 // why is this done????? = it breaks dialogs??
6037 //this.parent().el.setStyle('position', 'relative');
6041 this.footer.parentId = this.id;
6042 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6045 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6047 this.store.on('load', this.onLoad, this);
6048 this.store.on('beforeload', this.onBeforeLoad, this);
6049 this.store.on('update', this.onUpdate, this);
6050 this.store.on('add', this.onAdd, this);
6051 this.store.on("clear", this.clear, this);
6053 this.el.on("contextmenu", this.onContextMenu, this);
6055 this.mainBody.on('scroll', this.onBodyScroll, this);
6060 onContextMenu : function(e, t)
6062 this.processEvent("contextmenu", e);
6065 processEvent : function(name, e)
6067 if (name != 'touchstart' ) {
6068 this.fireEvent(name, e);
6071 var t = e.getTarget();
6073 var cell = Roo.get(t);
6079 if(cell.findParent('tfoot', false, true)){
6083 if(cell.findParent('thead', false, true)){
6085 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086 cell = Roo.get(t).findParent('th', false, true);
6088 Roo.log("failed to find th in thead?");
6089 Roo.log(e.getTarget());
6094 var cellIndex = cell.dom.cellIndex;
6096 var ename = name == 'touchstart' ? 'click' : name;
6097 this.fireEvent("header" + ename, this, cellIndex, e);
6102 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103 cell = Roo.get(t).findParent('td', false, true);
6105 Roo.log("failed to find th in tbody?");
6106 Roo.log(e.getTarget());
6111 var row = cell.findParent('tr', false, true);
6112 var cellIndex = cell.dom.cellIndex;
6113 var rowIndex = row.dom.rowIndex - 1;
6117 this.fireEvent("row" + name, this, rowIndex, e);
6121 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6127 onMouseover : function(e, el)
6129 var cell = Roo.get(el);
6135 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136 cell = cell.findParent('td', false, true);
6139 var row = cell.findParent('tr', false, true);
6140 var cellIndex = cell.dom.cellIndex;
6141 var rowIndex = row.dom.rowIndex - 1; // start from 0
6143 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6147 onMouseout : function(e, el)
6149 var cell = Roo.get(el);
6155 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156 cell = cell.findParent('td', false, true);
6159 var row = cell.findParent('tr', false, true);
6160 var cellIndex = cell.dom.cellIndex;
6161 var rowIndex = row.dom.rowIndex - 1; // start from 0
6163 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6167 onClick : function(e, el)
6169 var cell = Roo.get(el);
6171 if(!cell || (!this.cellSelection && !this.rowSelection)){
6175 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176 cell = cell.findParent('td', false, true);
6179 if(!cell || typeof(cell) == 'undefined'){
6183 var row = cell.findParent('tr', false, true);
6185 if(!row || typeof(row) == 'undefined'){
6189 var cellIndex = cell.dom.cellIndex;
6190 var rowIndex = this.getRowIndex(row);
6192 // why??? - should these not be based on SelectionModel?
6193 if(this.cellSelection){
6194 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6197 if(this.rowSelection){
6198 this.fireEvent('rowclick', this, row, rowIndex, e);
6204 onDblClick : function(e,el)
6206 var cell = Roo.get(el);
6208 if(!cell || (!this.cellSelection && !this.rowSelection)){
6212 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213 cell = cell.findParent('td', false, true);
6216 if(!cell || typeof(cell) == 'undefined'){
6220 var row = cell.findParent('tr', false, true);
6222 if(!row || typeof(row) == 'undefined'){
6226 var cellIndex = cell.dom.cellIndex;
6227 var rowIndex = this.getRowIndex(row);
6229 if(this.cellSelection){
6230 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6233 if(this.rowSelection){
6234 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6238 sort : function(e,el)
6240 var col = Roo.get(el);
6242 if(!col.hasClass('sortable')){
6246 var sort = col.attr('sort');
6249 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6253 this.store.sortInfo = {field : sort, direction : dir};
6256 Roo.log("calling footer first");
6257 this.footer.onClick('first');
6260 this.store.load({ params : { start : 0 } });
6264 renderHeader : function()
6272 this.totalWidth = 0;
6274 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6276 var config = cm.config[i];
6281 html: cm.getColumnHeader(i)
6286 if(typeof(config.sortable) != 'undefined' && config.sortable){
6288 c.html = '<i class="glyphicon"></i>' + c.html;
6291 if(typeof(config.lgHeader) != 'undefined'){
6292 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6295 if(typeof(config.mdHeader) != 'undefined'){
6296 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6299 if(typeof(config.smHeader) != 'undefined'){
6300 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6303 if(typeof(config.xsHeader) != 'undefined'){
6304 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6311 if(typeof(config.tooltip) != 'undefined'){
6312 c.tooltip = config.tooltip;
6315 if(typeof(config.colspan) != 'undefined'){
6316 c.colspan = config.colspan;
6319 if(typeof(config.hidden) != 'undefined' && config.hidden){
6320 c.style += ' display:none;';
6323 if(typeof(config.dataIndex) != 'undefined'){
6324 c.sort = config.dataIndex;
6329 if(typeof(config.align) != 'undefined' && config.align.length){
6330 c.style += ' text-align:' + config.align + ';';
6333 if(typeof(config.width) != 'undefined'){
6334 c.style += ' width:' + config.width + 'px;';
6335 this.totalWidth += config.width;
6337 this.totalWidth += 100; // assume minimum of 100 per column?
6340 if(typeof(config.cls) != 'undefined'){
6341 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6344 ['xs','sm','md','lg'].map(function(size){
6346 if(typeof(config[size]) == 'undefined'){
6350 if (!config[size]) { // 0 = hidden
6351 c.cls += ' hidden-' + size;
6355 c.cls += ' col-' + size + '-' + config[size];
6365 renderBody : function()
6375 colspan : this.cm.getColumnCount()
6385 renderFooter : function()
6395 colspan : this.cm.getColumnCount()
6409 // Roo.log('ds onload');
6414 var ds = this.store;
6416 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418 if (_this.store.sortInfo) {
6420 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421 e.select('i', true).addClass(['glyphicon-arrow-up']);
6424 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425 e.select('i', true).addClass(['glyphicon-arrow-down']);
6430 var tbody = this.mainBody;
6432 if(ds.getCount() > 0){
6433 ds.data.each(function(d,rowIndex){
6434 var row = this.renderRow(cm, ds, rowIndex);
6436 tbody.createChild(row);
6440 if(row.cellObjects.length){
6441 Roo.each(row.cellObjects, function(r){
6442 _this.renderCellObject(r);
6449 Roo.each(this.el.select('tbody td', true).elements, function(e){
6450 e.on('mouseover', _this.onMouseover, _this);
6453 Roo.each(this.el.select('tbody td', true).elements, function(e){
6454 e.on('mouseout', _this.onMouseout, _this);
6456 this.fireEvent('rowsrendered', this);
6457 //if(this.loadMask){
6458 // this.maskEl.hide();
6465 onUpdate : function(ds,record)
6467 this.refreshRow(record);
6471 onRemove : function(ds, record, index, isUpdate){
6472 if(isUpdate !== true){
6473 this.fireEvent("beforerowremoved", this, index, record);
6475 var bt = this.mainBody.dom;
6477 var rows = this.el.select('tbody > tr', true).elements;
6479 if(typeof(rows[index]) != 'undefined'){
6480 bt.removeChild(rows[index].dom);
6483 // if(bt.rows[index]){
6484 // bt.removeChild(bt.rows[index]);
6487 if(isUpdate !== true){
6488 //this.stripeRows(index);
6489 //this.syncRowHeights(index, index);
6491 this.fireEvent("rowremoved", this, index, record);
6495 onAdd : function(ds, records, rowIndex)
6497 //Roo.log('on Add called');
6498 // - note this does not handle multiple adding very well..
6499 var bt = this.mainBody.dom;
6500 for (var i =0 ; i < records.length;i++) {
6501 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502 //Roo.log(records[i]);
6503 //Roo.log(this.store.getAt(rowIndex+i));
6504 this.insertRow(this.store, rowIndex + i, false);
6511 refreshRow : function(record){
6512 var ds = this.store, index;
6513 if(typeof record == 'number'){
6515 record = ds.getAt(index);
6517 index = ds.indexOf(record);
6519 this.insertRow(ds, index, true);
6521 this.onRemove(ds, record, index+1, true);
6523 //this.syncRowHeights(index, index);
6525 this.fireEvent("rowupdated", this, index, record);
6528 insertRow : function(dm, rowIndex, isUpdate){
6531 this.fireEvent("beforerowsinserted", this, rowIndex);
6533 //var s = this.getScrollState();
6534 var row = this.renderRow(this.cm, this.store, rowIndex);
6535 // insert before rowIndex..
6536 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6540 if(row.cellObjects.length){
6541 Roo.each(row.cellObjects, function(r){
6542 _this.renderCellObject(r);
6547 this.fireEvent("rowsinserted", this, rowIndex);
6548 //this.syncRowHeights(firstRow, lastRow);
6549 //this.stripeRows(firstRow);
6556 getRowDom : function(rowIndex)
6558 var rows = this.el.select('tbody > tr', true).elements;
6560 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6563 // returns the object tree for a tr..
6566 renderRow : function(cm, ds, rowIndex)
6569 var d = ds.getAt(rowIndex);
6576 var cellObjects = [];
6578 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579 var config = cm.config[i];
6581 var renderer = cm.getRenderer(i);
6585 if(typeof(renderer) !== 'undefined'){
6586 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6588 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589 // and are rendered into the cells after the row is rendered - using the id for the element.
6591 if(typeof(value) === 'object'){
6601 rowIndex : rowIndex,
6606 this.fireEvent('rowclass', this, rowcfg);
6610 cls : rowcfg.rowClass,
6612 html: (typeof(value) === 'object') ? '' : value
6619 if(typeof(config.colspan) != 'undefined'){
6620 td.colspan = config.colspan;
6623 if(typeof(config.hidden) != 'undefined' && config.hidden){
6624 td.style += ' display:none;';
6627 if(typeof(config.align) != 'undefined' && config.align.length){
6628 td.style += ' text-align:' + config.align + ';';
6631 if(typeof(config.width) != 'undefined'){
6632 td.style += ' width:' + config.width + 'px;';
6635 if(typeof(config.cursor) != 'undefined'){
6636 td.style += ' cursor:' + config.cursor + ';';
6639 if(typeof(config.cls) != 'undefined'){
6640 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6643 ['xs','sm','md','lg'].map(function(size){
6645 if(typeof(config[size]) == 'undefined'){
6649 if (!config[size]) { // 0 = hidden
6650 td.cls += ' hidden-' + size;
6654 td.cls += ' col-' + size + '-' + config[size];
6662 row.cellObjects = cellObjects;
6670 onBeforeLoad : function()
6672 //Roo.log('ds onBeforeLoad');
6676 //if(this.loadMask){
6677 // this.maskEl.show();
6685 this.el.select('tbody', true).first().dom.innerHTML = '';
6688 * Show or hide a row.
6689 * @param {Number} rowIndex to show or hide
6690 * @param {Boolean} state hide
6692 setRowVisibility : function(rowIndex, state)
6694 var bt = this.mainBody.dom;
6696 var rows = this.el.select('tbody > tr', true).elements;
6698 if(typeof(rows[rowIndex]) == 'undefined'){
6701 rows[rowIndex].dom.style.display = state ? '' : 'none';
6705 getSelectionModel : function(){
6707 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6709 return this.selModel;
6712 * Render the Roo.bootstrap object from renderder
6714 renderCellObject : function(r)
6718 var t = r.cfg.render(r.container);
6721 Roo.each(r.cfg.cn, function(c){
6723 container: t.getChildContainer(),
6726 _this.renderCellObject(child);
6731 getRowIndex : function(row)
6735 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6746 * Returns the grid's underlying element = used by panel.Grid
6747 * @return {Element} The element
6749 getGridEl : function(){
6753 * Forces a resize - used by panel.Grid
6754 * @return {Element} The element
6756 autoSize : function()
6758 //var ctr = Roo.get(this.container.dom.parentElement);
6759 var ctr = Roo.get(this.el.dom);
6761 var thd = this.getGridEl().select('thead',true).first();
6762 var tbd = this.getGridEl().select('tbody', true).first();
6763 var tfd = this.getGridEl().select('tfoot', true).first();
6765 var cw = ctr.getWidth();
6769 tbd.setSize(ctr.getWidth(),
6770 ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6772 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6775 cw = Math.max(cw, this.totalWidth);
6776 this.getGridEl().select('tr',true).setWidth(cw);
6777 // resize 'expandable coloumn?
6779 return; // we doe not have a view in this design..
6782 onBodyScroll: function()
6785 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786 this.mainHead.setStyle({
6787 'position' : 'relative',
6788 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6805 * @class Roo.bootstrap.TableCell
6806 * @extends Roo.bootstrap.Component
6807 * Bootstrap TableCell class
6808 * @cfg {String} html cell contain text
6809 * @cfg {String} cls cell class
6810 * @cfg {String} tag cell tag (td|th) default td
6811 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812 * @cfg {String} align Aligns the content in a cell
6813 * @cfg {String} axis Categorizes cells
6814 * @cfg {String} bgcolor Specifies the background color of a cell
6815 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816 * @cfg {Number} colspan Specifies the number of columns a cell should span
6817 * @cfg {String} headers Specifies one or more header cells a cell is related to
6818 * @cfg {Number} height Sets the height of a cell
6819 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820 * @cfg {Number} rowspan Sets the number of rows a cell should span
6821 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822 * @cfg {String} valign Vertical aligns the content in a cell
6823 * @cfg {Number} width Specifies the width of a cell
6826 * Create a new TableCell
6827 * @param {Object} config The config object
6830 Roo.bootstrap.TableCell = function(config){
6831 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6854 getAutoCreate : function(){
6855 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6875 cfg.align=this.align
6881 cfg.bgcolor=this.bgcolor
6884 cfg.charoff=this.charoff
6887 cfg.colspan=this.colspan
6890 cfg.headers=this.headers
6893 cfg.height=this.height
6896 cfg.nowrap=this.nowrap
6899 cfg.rowspan=this.rowspan
6902 cfg.scope=this.scope
6905 cfg.valign=this.valign
6908 cfg.width=this.width
6927 * @class Roo.bootstrap.TableRow
6928 * @extends Roo.bootstrap.Component
6929 * Bootstrap TableRow class
6930 * @cfg {String} cls row class
6931 * @cfg {String} align Aligns the content in a table row
6932 * @cfg {String} bgcolor Specifies a background color for a table row
6933 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934 * @cfg {String} valign Vertical aligns the content in a table row
6937 * Create a new TableRow
6938 * @param {Object} config The config object
6941 Roo.bootstrap.TableRow = function(config){
6942 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6953 getAutoCreate : function(){
6954 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6964 cfg.align = this.align;
6967 cfg.bgcolor = this.bgcolor;
6970 cfg.charoff = this.charoff;
6973 cfg.valign = this.valign;
6991 * @class Roo.bootstrap.TableBody
6992 * @extends Roo.bootstrap.Component
6993 * Bootstrap TableBody class
6994 * @cfg {String} cls element class
6995 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996 * @cfg {String} align Aligns the content inside the element
6997 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7001 * Create a new TableBody
7002 * @param {Object} config The config object
7005 Roo.bootstrap.TableBody = function(config){
7006 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7017 getAutoCreate : function(){
7018 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7032 cfg.align = this.align;
7035 cfg.charoff = this.charoff;
7038 cfg.valign = this.valign;
7045 // initEvents : function()
7052 // this.store = Roo.factory(this.store, Roo.data);
7053 // this.store.on('load', this.onLoad, this);
7055 // this.store.load();
7059 // onLoad: function ()
7061 // this.fireEvent('load', this);
7071 * Ext JS Library 1.1.1
7072 * Copyright(c) 2006-2007, Ext JS, LLC.
7074 * Originally Released Under LGPL - original licence link has changed is not relivant.
7077 * <script type="text/javascript">
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7083 * @class Roo.form.Action
7084 * Internal Class used to handle form actions
7086 * @param {Roo.form.BasicForm} el The form element or its id
7087 * @param {Object} config Configuration options
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7095 this.options = options || {};
7098 * Client Validation Failed
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7103 * Server Validation Failed
7106 Roo.form.Action.SERVER_INVALID = 'server';
7108 * Connect to Server Failed
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7113 * Reading Data from Server Failed
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7118 Roo.form.Action.prototype = {
7120 failureType : undefined,
7121 response : undefined,
7125 run : function(options){
7130 success : function(response){
7135 handleResponse : function(response){
7139 // default connection failure
7140 failure : function(response){
7142 this.response = response;
7143 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144 this.form.afterAction(this, false);
7147 processResponse : function(response){
7148 this.response = response;
7149 if(!response.responseText){
7152 this.result = this.handleResponse(response);
7156 // utility functions used internally
7157 getUrl : function(appendParams){
7158 var url = this.options.url || this.form.url || this.form.el.dom.action;
7160 var p = this.getParams();
7162 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7168 getMethod : function(){
7169 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7172 getParams : function(){
7173 var bp = this.form.baseParams;
7174 var p = this.options.params;
7176 if(typeof p == "object"){
7177 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178 }else if(typeof p == 'string' && bp){
7179 p += '&' + Roo.urlEncode(bp);
7182 p = Roo.urlEncode(bp);
7187 createCallback : function(){
7189 success: this.success,
7190 failure: this.failure,
7192 timeout: (this.form.timeout*1000),
7193 upload: this.form.fileUpload ? this.success : undefined
7198 Roo.form.Action.Submit = function(form, options){
7199 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7205 haveProgress : false,
7206 uploadComplete : false,
7208 // uploadProgress indicator.
7209 uploadProgress : function()
7211 if (!this.form.progressUrl) {
7215 if (!this.haveProgress) {
7216 Roo.MessageBox.progress("Uploading", "Uploading");
7218 if (this.uploadComplete) {
7219 Roo.MessageBox.hide();
7223 this.haveProgress = true;
7225 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7227 var c = new Roo.data.Connection();
7229 url : this.form.progressUrl,
7234 success : function(req){
7235 //console.log(data);
7239 rdata = Roo.decode(req.responseText)
7241 Roo.log("Invalid data from server..");
7245 if (!rdata || !rdata.success) {
7247 Roo.MessageBox.alert(Roo.encode(rdata));
7250 var data = rdata.data;
7252 if (this.uploadComplete) {
7253 Roo.MessageBox.hide();
7258 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7262 this.uploadProgress.defer(2000,this);
7265 failure: function(data) {
7266 Roo.log('progress url failed ');
7277 // run get Values on the form, so it syncs any secondary forms.
7278 this.form.getValues();
7280 var o = this.options;
7281 var method = this.getMethod();
7282 var isPost = method == 'POST';
7283 if(o.clientValidation === false || this.form.isValid()){
7285 if (this.form.progressUrl) {
7286 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287 (new Date() * 1) + '' + Math.random());
7292 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293 form:this.form.el.dom,
7294 url:this.getUrl(!isPost),
7296 params:isPost ? this.getParams() : null,
7297 isUpload: this.form.fileUpload
7300 this.uploadProgress();
7302 }else if (o.clientValidation !== false){ // client validation failed
7303 this.failureType = Roo.form.Action.CLIENT_INVALID;
7304 this.form.afterAction(this, false);
7308 success : function(response)
7310 this.uploadComplete= true;
7311 if (this.haveProgress) {
7312 Roo.MessageBox.hide();
7316 var result = this.processResponse(response);
7317 if(result === true || result.success){
7318 this.form.afterAction(this, true);
7322 this.form.markInvalid(result.errors);
7323 this.failureType = Roo.form.Action.SERVER_INVALID;
7325 this.form.afterAction(this, false);
7327 failure : function(response)
7329 this.uploadComplete= true;
7330 if (this.haveProgress) {
7331 Roo.MessageBox.hide();
7334 this.response = response;
7335 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336 this.form.afterAction(this, false);
7339 handleResponse : function(response){
7340 if(this.form.errorReader){
7341 var rs = this.form.errorReader.read(response);
7344 for(var i = 0, len = rs.records.length; i < len; i++) {
7345 var r = rs.records[i];
7349 if(errors.length < 1){
7353 success : rs.success,
7359 ret = Roo.decode(response.responseText);
7363 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7373 Roo.form.Action.Load = function(form, options){
7374 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375 this.reader = this.form.reader;
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7383 Roo.Ajax.request(Roo.apply(
7384 this.createCallback(), {
7385 method:this.getMethod(),
7386 url:this.getUrl(false),
7387 params:this.getParams()
7391 success : function(response){
7393 var result = this.processResponse(response);
7394 if(result === true || !result.success || !result.data){
7395 this.failureType = Roo.form.Action.LOAD_FAILURE;
7396 this.form.afterAction(this, false);
7399 this.form.clearInvalid();
7400 this.form.setValues(result.data);
7401 this.form.afterAction(this, true);
7404 handleResponse : function(response){
7405 if(this.form.reader){
7406 var rs = this.form.reader.read(response);
7407 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7409 success : rs.success,
7413 return Roo.decode(response.responseText);
7417 Roo.form.Action.ACTION_TYPES = {
7418 'load' : Roo.form.Action.Load,
7419 'submit' : Roo.form.Action.Submit
7428 * @class Roo.bootstrap.Form
7429 * @extends Roo.bootstrap.Component
7430 * Bootstrap Form class
7431 * @cfg {String} method GET | POST (default POST)
7432 * @cfg {String} labelAlign top | left (default top)
7433 * @cfg {String} align left | right - for navbars
7434 * @cfg {Boolean} loadMask load mask when submit (default true)
7439 * @param {Object} config The config object
7443 Roo.bootstrap.Form = function(config){
7444 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7447 * @event clientvalidation
7448 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449 * @param {Form} this
7450 * @param {Boolean} valid true if the form has passed client-side validation
7452 clientvalidation: true,
7454 * @event beforeaction
7455 * Fires before any action is performed. Return false to cancel the action.
7456 * @param {Form} this
7457 * @param {Action} action The action to be performed
7461 * @event actionfailed
7462 * Fires when an action fails.
7463 * @param {Form} this
7464 * @param {Action} action The action that failed
7466 actionfailed : true,
7468 * @event actioncomplete
7469 * Fires when an action is completed.
7470 * @param {Form} this
7471 * @param {Action} action The action that completed
7473 actioncomplete : true
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7481 * @cfg {String} method
7482 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7487 * The URL to use for form actions if one isn't supplied in the action options.
7490 * @cfg {Boolean} fileUpload
7491 * Set to true if this form is a file upload.
7495 * @cfg {Object} baseParams
7496 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504 * @cfg {Sting} align (left|right) for navbar forms
7509 activeAction : null,
7512 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513 * element by passing it or its id or mask the form itself by passing in true.
7516 waitMsgTarget : false,
7521 * @cfg {Boolean} errPopover (true|false) default false
7525 getAutoCreate : function(){
7529 method : this.method || 'POST',
7530 id : this.id || Roo.id(),
7533 if (this.parent().xtype.match(/^Nav/)) {
7534 cfg.cls = 'navbar-form navbar-' + this.align;
7538 if (this.labelAlign == 'left' ) {
7539 cfg.cls += ' form-horizontal';
7545 initEvents : function()
7547 this.el.on('submit', this.onSubmit, this);
7548 // this was added as random key presses on the form where triggering form submit.
7549 this.el.on('keypress', function(e) {
7550 if (e.getCharCode() != 13) {
7553 // we might need to allow it for textareas.. and some other items.
7554 // check e.getTarget().
7556 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560 Roo.log("keypress blocked");
7568 onSubmit : function(e){
7573 * Returns true if client-side validation on the form is successful.
7576 isValid : function(){
7577 var items = this.getItems();
7581 items.each(function(f){
7595 if(this.errPopover && !valid){
7596 this.showErrPopover(target);
7602 showErrPopover : function(target)
7604 if(!this.errPopover){
7608 var oIndex = target.el.getStyle('z-index');
7610 target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7612 target.el.addClass('roo-invalid-outline');
7614 target.inputEl().focus();
7616 var fadeout = function(){
7618 target.inputEl().un('blur', fadeout);
7619 target.inputEl().un('keyup', fadeout);
7621 target.el.setStyle('z-index', oIndex);
7623 target.el.removeClass('roo-invalid-outline');
7627 target.inputEl().on('blur', fadeout, target);
7628 target.inputEl().on('keyup', fadeout, target);
7634 * Returns true if any fields in this form have changed since their original load.
7637 isDirty : function(){
7639 var items = this.getItems();
7640 items.each(function(f){
7650 * Performs a predefined action (submit or load) or custom actions you define on this form.
7651 * @param {String} actionName The name of the action type
7652 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7653 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7654 * accept other config options):
7656 Property Type Description
7657 ---------------- --------------- ----------------------------------------------------------------------------------
7658 url String The url for the action (defaults to the form's url)
7659 method String The form method to use (defaults to the form's method, or POST if not defined)
7660 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7661 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7662 validate the form on the client (defaults to false)
7664 * @return {BasicForm} this
7666 doAction : function(action, options){
7667 if(typeof action == 'string'){
7668 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7670 if(this.fireEvent('beforeaction', this, action) !== false){
7671 this.beforeAction(action);
7672 action.run.defer(100, action);
7678 beforeAction : function(action){
7679 var o = action.options;
7682 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7684 // not really supported yet.. ??
7686 //if(this.waitMsgTarget === true){
7687 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7688 //}else if(this.waitMsgTarget){
7689 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7690 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7692 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7698 afterAction : function(action, success){
7699 this.activeAction = null;
7700 var o = action.options;
7702 //if(this.waitMsgTarget === true){
7704 //}else if(this.waitMsgTarget){
7705 // this.waitMsgTarget.unmask();
7707 // Roo.MessageBox.updateProgress(1);
7708 // Roo.MessageBox.hide();
7715 Roo.callback(o.success, o.scope, [this, action]);
7716 this.fireEvent('actioncomplete', this, action);
7720 // failure condition..
7721 // we have a scenario where updates need confirming.
7722 // eg. if a locking scenario exists..
7723 // we look for { errors : { needs_confirm : true }} in the response.
7725 (typeof(action.result) != 'undefined') &&
7726 (typeof(action.result.errors) != 'undefined') &&
7727 (typeof(action.result.errors.needs_confirm) != 'undefined')
7730 Roo.log("not supported yet");
7733 Roo.MessageBox.confirm(
7734 "Change requires confirmation",
7735 action.result.errorMsg,
7740 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7750 Roo.callback(o.failure, o.scope, [this, action]);
7751 // show an error message if no failed handler is set..
7752 if (!this.hasListener('actionfailed')) {
7753 Roo.log("need to add dialog support");
7755 Roo.MessageBox.alert("Error",
7756 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7757 action.result.errorMsg :
7758 "Saving Failed, please check your entries or try again"
7763 this.fireEvent('actionfailed', this, action);
7768 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7769 * @param {String} id The value to search for
7772 findField : function(id){
7773 var items = this.getItems();
7774 var field = items.get(id);
7776 items.each(function(f){
7777 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7784 return field || null;
7787 * Mark fields in this form invalid in bulk.
7788 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7789 * @return {BasicForm} this
7791 markInvalid : function(errors){
7792 if(errors instanceof Array){
7793 for(var i = 0, len = errors.length; i < len; i++){
7794 var fieldError = errors[i];
7795 var f = this.findField(fieldError.id);
7797 f.markInvalid(fieldError.msg);
7803 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7804 field.markInvalid(errors[id]);
7808 //Roo.each(this.childForms || [], function (f) {
7809 // f.markInvalid(errors);
7816 * Set values for fields in this form in bulk.
7817 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7818 * @return {BasicForm} this
7820 setValues : function(values){
7821 if(values instanceof Array){ // array of objects
7822 for(var i = 0, len = values.length; i < len; i++){
7824 var f = this.findField(v.id);
7826 f.setValue(v.value);
7827 if(this.trackResetOnLoad){
7828 f.originalValue = f.getValue();
7832 }else{ // object hash
7835 if(typeof values[id] != 'function' && (field = this.findField(id))){
7837 if (field.setFromData &&
7839 field.displayField &&
7840 // combos' with local stores can
7841 // be queried via setValue()
7842 // to set their value..
7843 (field.store && !field.store.isLocal)
7847 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7848 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7849 field.setFromData(sd);
7852 field.setValue(values[id]);
7856 if(this.trackResetOnLoad){
7857 field.originalValue = field.getValue();
7863 //Roo.each(this.childForms || [], function (f) {
7864 // f.setValues(values);
7871 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7872 * they are returned as an array.
7873 * @param {Boolean} asString
7876 getValues : function(asString){
7877 //if (this.childForms) {
7878 // copy values from the child forms
7879 // Roo.each(this.childForms, function (f) {
7880 // this.setValues(f.getValues());
7886 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7887 if(asString === true){
7890 return Roo.urlDecode(fs);
7894 * Returns the fields in this form as an object with key/value pairs.
7895 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7898 getFieldValues : function(with_hidden)
7900 var items = this.getItems();
7902 items.each(function(f){
7906 var v = f.getValue();
7907 if (f.inputType =='radio') {
7908 if (typeof(ret[f.getName()]) == 'undefined') {
7909 ret[f.getName()] = ''; // empty..
7912 if (!f.el.dom.checked) {
7920 // not sure if this supported any more..
7921 if ((typeof(v) == 'object') && f.getRawValue) {
7922 v = f.getRawValue() ; // dates..
7924 // combo boxes where name != hiddenName...
7925 if (f.name != f.getName()) {
7926 ret[f.name] = f.getRawValue();
7928 ret[f.getName()] = v;
7935 * Clears all invalid messages in this form.
7936 * @return {BasicForm} this
7938 clearInvalid : function(){
7939 var items = this.getItems();
7941 items.each(function(f){
7952 * @return {BasicForm} this
7955 var items = this.getItems();
7956 items.each(function(f){
7960 Roo.each(this.childForms || [], function (f) {
7967 getItems : function()
7969 var r=new Roo.util.MixedCollection(false, function(o){
7970 return o.id || (o.id = Roo.id());
7972 var iter = function(el) {
7979 Roo.each(el.items,function(e) {
7997 * Ext JS Library 1.1.1
7998 * Copyright(c) 2006-2007, Ext JS, LLC.
8000 * Originally Released Under LGPL - original licence link has changed is not relivant.
8003 * <script type="text/javascript">
8006 * @class Roo.form.VTypes
8007 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8010 Roo.form.VTypes = function(){
8011 // closure these in so they are only created once.
8012 var alpha = /^[a-zA-Z_]+$/;
8013 var alphanum = /^[a-zA-Z0-9_]+$/;
8014 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8015 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8017 // All these messages and functions are configurable
8020 * The function used to validate email addresses
8021 * @param {String} value The email address
8023 'email' : function(v){
8024 return email.test(v);
8027 * The error text to display when the email validation function returns false
8030 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8032 * The keystroke filter mask to be applied on email input
8035 'emailMask' : /[a-z0-9_\.\-@]/i,
8038 * The function used to validate URLs
8039 * @param {String} value The URL
8041 'url' : function(v){
8045 * The error text to display when the url validation function returns false
8048 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8051 * The function used to validate alpha values
8052 * @param {String} value The value
8054 'alpha' : function(v){
8055 return alpha.test(v);
8058 * The error text to display when the alpha validation function returns false
8061 'alphaText' : 'This field should only contain letters and _',
8063 * The keystroke filter mask to be applied on alpha input
8066 'alphaMask' : /[a-z_]/i,
8069 * The function used to validate alphanumeric values
8070 * @param {String} value The value
8072 'alphanum' : function(v){
8073 return alphanum.test(v);
8076 * The error text to display when the alphanumeric validation function returns false
8079 'alphanumText' : 'This field should only contain letters, numbers and _',
8081 * The keystroke filter mask to be applied on alphanumeric input
8084 'alphanumMask' : /[a-z0-9_]/i
8094 * @class Roo.bootstrap.Input
8095 * @extends Roo.bootstrap.Component
8096 * Bootstrap Input class
8097 * @cfg {Boolean} disabled is it disabled
8098 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8099 * @cfg {String} name name of the input
8100 * @cfg {string} fieldLabel - the label associated
8101 * @cfg {string} placeholder - placeholder to put in text.
8102 * @cfg {string} before - input group add on before
8103 * @cfg {string} after - input group add on after
8104 * @cfg {string} size - (lg|sm) or leave empty..
8105 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8106 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8107 * @cfg {Number} md colspan out of 12 for computer-sized screens
8108 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8109 * @cfg {string} value default value of the input
8110 * @cfg {Number} labelWidth set the width of label (0-12)
8111 * @cfg {String} labelAlign (top|left)
8112 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8113 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8114 * @cfg {String} indicatorpos (left|right) default left
8116 * @cfg {String} align (left|center|right) Default left
8117 * @cfg {Boolean} forceFeedback (true|false) Default false
8123 * Create a new Input
8124 * @param {Object} config The config object
8127 Roo.bootstrap.Input = function(config){
8128 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8133 * Fires when this field receives input focus.
8134 * @param {Roo.form.Field} this
8139 * Fires when this field loses input focus.
8140 * @param {Roo.form.Field} this
8145 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8146 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8147 * @param {Roo.form.Field} this
8148 * @param {Roo.EventObject} e The event object
8153 * Fires just before the field blurs if the field value has changed.
8154 * @param {Roo.form.Field} this
8155 * @param {Mixed} newValue The new value
8156 * @param {Mixed} oldValue The original value
8161 * Fires after the field has been marked as invalid.
8162 * @param {Roo.form.Field} this
8163 * @param {String} msg The validation message
8168 * Fires after the field has been validated with no errors.
8169 * @param {Roo.form.Field} this
8174 * Fires after the key up
8175 * @param {Roo.form.Field} this
8176 * @param {Roo.EventObject} e The event Object
8182 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8184 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8185 automatic validation (defaults to "keyup").
8187 validationEvent : "keyup",
8189 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8191 validateOnBlur : true,
8193 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8195 validationDelay : 250,
8197 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8199 focusClass : "x-form-focus", // not needed???
8203 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8205 invalidClass : "has-warning",
8208 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8210 validClass : "has-success",
8213 * @cfg {Boolean} hasFeedback (true|false) default true
8218 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8220 invalidFeedbackClass : "glyphicon-warning-sign",
8223 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8225 validFeedbackClass : "glyphicon-ok",
8228 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8230 selectOnFocus : false,
8233 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8237 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8242 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8244 disableKeyFilter : false,
8247 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8251 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8255 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8257 blankText : "This field is required",
8260 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8264 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8266 maxLength : Number.MAX_VALUE,
8268 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8270 minLengthText : "The minimum length for this field is {0}",
8272 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8274 maxLengthText : "The maximum length for this field is {0}",
8278 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8279 * If available, this function will be called only after the basic validators all return true, and will be passed the
8280 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8284 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8285 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8286 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8290 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8294 autocomplete: false,
8313 formatedValue : false,
8314 forceFeedback : false,
8316 indicatorpos : 'left',
8318 parentLabelAlign : function()
8321 while (parent.parent()) {
8322 parent = parent.parent();
8323 if (typeof(parent.labelAlign) !='undefined') {
8324 return parent.labelAlign;
8331 getAutoCreate : function()
8333 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8339 if(this.inputType != 'hidden'){
8340 cfg.cls = 'form-group' //input-group
8346 type : this.inputType,
8348 cls : 'form-control',
8349 placeholder : this.placeholder || '',
8350 autocomplete : this.autocomplete || 'new-password'
8354 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8357 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8358 input.maxLength = this.maxLength;
8361 if (this.disabled) {
8362 input.disabled=true;
8365 if (this.readOnly) {
8366 input.readonly=true;
8370 input.name = this.name;
8374 input.cls += ' input-' + this.size;
8378 ['xs','sm','md','lg'].map(function(size){
8379 if (settings[size]) {
8380 cfg.cls += ' col-' + size + '-' + settings[size];
8384 var inputblock = input;
8388 cls: 'glyphicon form-control-feedback'
8391 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8394 cls : 'has-feedback',
8402 if (this.before || this.after) {
8405 cls : 'input-group',
8409 if (this.before && typeof(this.before) == 'string') {
8411 inputblock.cn.push({
8413 cls : 'roo-input-before input-group-addon',
8417 if (this.before && typeof(this.before) == 'object') {
8418 this.before = Roo.factory(this.before);
8420 inputblock.cn.push({
8422 cls : 'roo-input-before input-group-' +
8423 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8427 inputblock.cn.push(input);
8429 if (this.after && typeof(this.after) == 'string') {
8430 inputblock.cn.push({
8432 cls : 'roo-input-after input-group-addon',
8436 if (this.after && typeof(this.after) == 'object') {
8437 this.after = Roo.factory(this.after);
8439 inputblock.cn.push({
8441 cls : 'roo-input-after input-group-' +
8442 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8446 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8447 inputblock.cls += ' has-feedback';
8448 inputblock.cn.push(feedback);
8452 if (align ==='left' && this.fieldLabel.length) {
8457 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8458 tooltip : 'This field is required'
8463 cls : 'control-label col-sm-' + this.labelWidth,
8464 html : this.fieldLabel
8468 cls : "col-sm-" + (12 - this.labelWidth),
8476 if(this.indicatorpos == 'right'){
8481 cls : 'control-label col-sm-' + this.labelWidth,
8482 html : this.fieldLabel
8487 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8488 tooltip : 'This field is required'
8491 cls : "col-sm-" + (12 - this.labelWidth),
8500 } else if ( this.fieldLabel.length) {
8505 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8506 tooltip : 'This field is required'
8510 //cls : 'input-group-addon',
8511 html : this.fieldLabel
8519 if(this.indicatorpos == 'right'){
8524 //cls : 'input-group-addon',
8525 html : this.fieldLabel
8530 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8531 tooltip : 'This field is required'
8551 if (this.parentType === 'Navbar' && this.parent().bar) {
8552 cfg.cls += ' navbar-form';
8555 if (this.parentType === 'NavGroup') {
8556 cfg.cls += ' navbar-form';
8564 * return the real input element.
8566 inputEl: function ()
8568 return this.el.select('input.form-control',true).first();
8571 tooltipEl : function()
8573 return this.inputEl();
8576 indicatorEl : function()
8578 var indicator = this.el.select('i.roo-required-indicator',true).first();
8588 setDisabled : function(v)
8590 var i = this.inputEl().dom;
8592 i.removeAttribute('disabled');
8596 i.setAttribute('disabled','true');
8598 initEvents : function()
8601 this.inputEl().on("keydown" , this.fireKey, this);
8602 this.inputEl().on("focus", this.onFocus, this);
8603 this.inputEl().on("blur", this.onBlur, this);
8605 this.inputEl().relayEvent('keyup', this);
8607 this.indicator = this.indicatorEl();
8610 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8611 this.indicator.hide();
8614 // reference to original value for reset
8615 this.originalValue = this.getValue();
8616 //Roo.form.TextField.superclass.initEvents.call(this);
8617 if(this.validationEvent == 'keyup'){
8618 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8619 this.inputEl().on('keyup', this.filterValidation, this);
8621 else if(this.validationEvent !== false){
8622 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8625 if(this.selectOnFocus){
8626 this.on("focus", this.preFocus, this);
8629 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8630 this.inputEl().on("keypress", this.filterKeys, this);
8632 this.inputEl().relayEvent('keypress', this);
8635 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8636 this.el.on("click", this.autoSize, this);
8639 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8640 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8643 if (typeof(this.before) == 'object') {
8644 this.before.render(this.el.select('.roo-input-before',true).first());
8646 if (typeof(this.after) == 'object') {
8647 this.after.render(this.el.select('.roo-input-after',true).first());
8652 filterValidation : function(e){
8653 if(!e.isNavKeyPress()){
8654 this.validationTask.delay(this.validationDelay);
8658 * Validates the field value
8659 * @return {Boolean} True if the value is valid, else false
8661 validate : function(){
8662 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8663 if(this.disabled || this.validateValue(this.getRawValue())){
8674 * Validates a value according to the field's validation rules and marks the field as invalid
8675 * if the validation fails
8676 * @param {Mixed} value The value to validate
8677 * @return {Boolean} True if the value is valid, else false
8679 validateValue : function(value){
8680 if(value.length < 1) { // if it's blank
8681 if(this.allowBlank){
8687 if(value.length < this.minLength){
8690 if(value.length > this.maxLength){
8694 var vt = Roo.form.VTypes;
8695 if(!vt[this.vtype](value, this)){
8699 if(typeof this.validator == "function"){
8700 var msg = this.validator(value);
8706 if(this.regex && !this.regex.test(value)){
8716 fireKey : function(e){
8717 //Roo.log('field ' + e.getKey());
8718 if(e.isNavKeyPress()){
8719 this.fireEvent("specialkey", this, e);
8722 focus : function (selectText){
8724 this.inputEl().focus();
8725 if(selectText === true){
8726 this.inputEl().dom.select();
8732 onFocus : function(){
8733 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8734 // this.el.addClass(this.focusClass);
8737 this.hasFocus = true;
8738 this.startValue = this.getValue();
8739 this.fireEvent("focus", this);
8743 beforeBlur : Roo.emptyFn,
8747 onBlur : function(){
8749 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8750 //this.el.removeClass(this.focusClass);
8752 this.hasFocus = false;
8753 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8756 var v = this.getValue();
8757 if(String(v) !== String(this.startValue)){
8758 this.fireEvent('change', this, v, this.startValue);
8760 this.fireEvent("blur", this);
8764 * Resets the current field value to the originally loaded value and clears any validation messages
8767 this.setValue(this.originalValue);
8771 * Returns the name of the field
8772 * @return {Mixed} name The name field
8774 getName: function(){
8778 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8779 * @return {Mixed} value The field value
8781 getValue : function(){
8783 var v = this.inputEl().getValue();
8788 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8789 * @return {Mixed} value The field value
8791 getRawValue : function(){
8792 var v = this.inputEl().getValue();
8798 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8799 * @param {Mixed} value The value to set
8801 setRawValue : function(v){
8802 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8805 selectText : function(start, end){
8806 var v = this.getRawValue();
8808 start = start === undefined ? 0 : start;
8809 end = end === undefined ? v.length : end;
8810 var d = this.inputEl().dom;
8811 if(d.setSelectionRange){
8812 d.setSelectionRange(start, end);
8813 }else if(d.createTextRange){
8814 var range = d.createTextRange();
8815 range.moveStart("character", start);
8816 range.moveEnd("character", v.length-end);
8823 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8824 * @param {Mixed} value The value to set
8826 setValue : function(v){
8829 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8835 processValue : function(value){
8836 if(this.stripCharsRe){
8837 var newValue = value.replace(this.stripCharsRe, '');
8838 if(newValue !== value){
8839 this.setRawValue(newValue);
8846 preFocus : function(){
8848 if(this.selectOnFocus){
8849 this.inputEl().dom.select();
8852 filterKeys : function(e){
8854 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8857 var c = e.getCharCode(), cc = String.fromCharCode(c);
8858 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8861 if(!this.maskRe.test(cc)){
8866 * Clear any invalid styles/messages for this field
8868 clearInvalid : function(){
8870 if(!this.el || this.preventMark){ // not rendered
8875 this.indicator.hide();
8878 this.el.removeClass(this.invalidClass);
8880 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8882 var feedback = this.el.select('.form-control-feedback', true).first();
8885 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8890 this.fireEvent('valid', this);
8894 * Mark this field as valid
8896 markValid : function()
8898 if(!this.el || this.preventMark){ // not rendered
8902 this.el.removeClass([this.invalidClass, this.validClass]);
8904 var feedback = this.el.select('.form-control-feedback', true).first();
8907 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8914 if(this.allowBlank && !this.getRawValue().length){
8919 this.indicator.hide();
8922 this.el.addClass(this.validClass);
8924 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8926 var feedback = this.el.select('.form-control-feedback', true).first();
8929 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8930 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8935 this.fireEvent('valid', this);
8939 * Mark this field as invalid
8940 * @param {String} msg The validation message
8942 markInvalid : function(msg)
8944 if(!this.el || this.preventMark){ // not rendered
8948 this.el.removeClass([this.invalidClass, this.validClass]);
8950 var feedback = this.el.select('.form-control-feedback', true).first();
8953 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8960 if(this.allowBlank && !this.getRawValue().length){
8965 this.indicator.show();
8968 this.el.addClass(this.invalidClass);
8970 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8972 var feedback = this.el.select('.form-control-feedback', true).first();
8975 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8977 if(this.getValue().length || this.forceFeedback){
8978 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8985 this.fireEvent('invalid', this, msg);
8988 SafariOnKeyDown : function(event)
8990 // this is a workaround for a password hang bug on chrome/ webkit.
8991 if (this.inputEl().dom.type != 'password') {
8995 var isSelectAll = false;
8997 if(this.inputEl().dom.selectionEnd > 0){
8998 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9000 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9001 event.preventDefault();
9006 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9008 event.preventDefault();
9009 // this is very hacky as keydown always get's upper case.
9011 var cc = String.fromCharCode(event.getCharCode());
9012 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9016 adjustWidth : function(tag, w){
9017 tag = tag.toLowerCase();
9018 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9019 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9023 if(tag == 'textarea'){
9026 }else if(Roo.isOpera){
9030 if(tag == 'textarea'){
9049 * @class Roo.bootstrap.TextArea
9050 * @extends Roo.bootstrap.Input
9051 * Bootstrap TextArea class
9052 * @cfg {Number} cols Specifies the visible width of a text area
9053 * @cfg {Number} rows Specifies the visible number of lines in a text area
9054 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9055 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9056 * @cfg {string} html text
9059 * Create a new TextArea
9060 * @param {Object} config The config object
9063 Roo.bootstrap.TextArea = function(config){
9064 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9068 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9078 getAutoCreate : function(){
9080 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9091 value : this.value || '',
9092 html: this.html || '',
9093 cls : 'form-control',
9094 placeholder : this.placeholder || ''
9098 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9099 input.maxLength = this.maxLength;
9103 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9107 input.cols = this.cols;
9110 if (this.readOnly) {
9111 input.readonly = true;
9115 input.name = this.name;
9119 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9123 ['xs','sm','md','lg'].map(function(size){
9124 if (settings[size]) {
9125 cfg.cls += ' col-' + size + '-' + settings[size];
9129 var inputblock = input;
9131 if(this.hasFeedback && !this.allowBlank){
9135 cls: 'glyphicon form-control-feedback'
9139 cls : 'has-feedback',
9148 if (this.before || this.after) {
9151 cls : 'input-group',
9155 inputblock.cn.push({
9157 cls : 'input-group-addon',
9162 inputblock.cn.push(input);
9164 if(this.hasFeedback && !this.allowBlank){
9165 inputblock.cls += ' has-feedback';
9166 inputblock.cn.push(feedback);
9170 inputblock.cn.push({
9172 cls : 'input-group-addon',
9179 if (align ==='left' && this.fieldLabel.length) {
9180 // Roo.log("left and has label");
9186 cls : 'control-label col-sm-' + this.labelWidth,
9187 html : this.fieldLabel
9191 cls : "col-sm-" + (12 - this.labelWidth),
9198 } else if ( this.fieldLabel.length) {
9199 // Roo.log(" label");
9204 //cls : 'input-group-addon',
9205 html : this.fieldLabel
9215 // Roo.log(" no label && no align");
9225 if (this.disabled) {
9226 input.disabled=true;
9233 * return the real textarea element.
9235 inputEl: function ()
9237 return this.el.select('textarea.form-control',true).first();
9241 * Clear any invalid styles/messages for this field
9243 clearInvalid : function()
9246 if(!this.el || this.preventMark){ // not rendered
9250 var label = this.el.select('label', true).first();
9251 var icon = this.el.select('i.fa-star', true).first();
9257 this.el.removeClass(this.invalidClass);
9259 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261 var feedback = this.el.select('.form-control-feedback', true).first();
9264 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9269 this.fireEvent('valid', this);
9273 * Mark this field as valid
9275 markValid : function()
9277 if(!this.el || this.preventMark){ // not rendered
9281 this.el.removeClass([this.invalidClass, this.validClass]);
9283 var feedback = this.el.select('.form-control-feedback', true).first();
9286 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9289 if(this.disabled || this.allowBlank){
9293 var label = this.el.select('label', true).first();
9294 var icon = this.el.select('i.fa-star', true).first();
9300 this.el.addClass(this.validClass);
9302 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9304 var feedback = this.el.select('.form-control-feedback', true).first();
9307 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9308 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9313 this.fireEvent('valid', this);
9317 * Mark this field as invalid
9318 * @param {String} msg The validation message
9320 markInvalid : function(msg)
9322 if(!this.el || this.preventMark){ // not rendered
9326 this.el.removeClass([this.invalidClass, this.validClass]);
9328 var feedback = this.el.select('.form-control-feedback', true).first();
9331 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9334 if(this.disabled || this.allowBlank){
9338 var label = this.el.select('label', true).first();
9339 var icon = this.el.select('i.fa-star', true).first();
9341 if(!this.getValue().length && label && !icon){
9342 this.el.createChild({
9344 cls : 'text-danger fa fa-lg fa-star',
9345 tooltip : 'This field is required',
9346 style : 'margin-right:5px;'
9350 this.el.addClass(this.invalidClass);
9352 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9354 var feedback = this.el.select('.form-control-feedback', true).first();
9357 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9359 if(this.getValue().length || this.forceFeedback){
9360 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9367 this.fireEvent('invalid', this, msg);
9375 * trigger field - base class for combo..
9380 * @class Roo.bootstrap.TriggerField
9381 * @extends Roo.bootstrap.Input
9382 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9383 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9384 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9385 * for which you can provide a custom implementation. For example:
9387 var trigger = new Roo.bootstrap.TriggerField();
9388 trigger.onTriggerClick = myTriggerFn;
9389 trigger.applyTo('my-field');
9392 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9393 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9394 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9395 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9396 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9399 * Create a new TriggerField.
9400 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9401 * to the base TextField)
9403 Roo.bootstrap.TriggerField = function(config){
9404 this.mimicing = false;
9405 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9408 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9410 * @cfg {String} triggerClass A CSS class to apply to the trigger
9413 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9418 * @cfg {Boolean} removable (true|false) special filter default false
9422 /** @cfg {Boolean} grow @hide */
9423 /** @cfg {Number} growMin @hide */
9424 /** @cfg {Number} growMax @hide */
9430 autoSize: Roo.emptyFn,
9437 actionMode : 'wrap',
9442 getAutoCreate : function(){
9444 var align = this.labelAlign || this.parentLabelAlign();
9449 cls: 'form-group' //input-group
9456 type : this.inputType,
9457 cls : 'form-control',
9458 autocomplete: 'new-password',
9459 placeholder : this.placeholder || ''
9463 input.name = this.name;
9466 input.cls += ' input-' + this.size;
9469 if (this.disabled) {
9470 input.disabled=true;
9473 var inputblock = input;
9475 if(this.hasFeedback && !this.allowBlank){
9479 cls: 'glyphicon form-control-feedback'
9482 if(this.removable && !this.editable && !this.tickable){
9484 cls : 'has-feedback',
9490 cls : 'roo-combo-removable-btn close'
9497 cls : 'has-feedback',
9506 if(this.removable && !this.editable && !this.tickable){
9508 cls : 'roo-removable',
9514 cls : 'roo-combo-removable-btn close'
9521 if (this.before || this.after) {
9524 cls : 'input-group',
9528 inputblock.cn.push({
9530 cls : 'input-group-addon',
9535 inputblock.cn.push(input);
9537 if(this.hasFeedback && !this.allowBlank){
9538 inputblock.cls += ' has-feedback';
9539 inputblock.cn.push(feedback);
9543 inputblock.cn.push({
9545 cls : 'input-group-addon',
9558 cls: 'form-hidden-field'
9572 cls: 'form-hidden-field'
9576 cls: 'roo-select2-choices',
9580 cls: 'roo-select2-search-field',
9593 cls: 'roo-select2-container input-group',
9598 // cls: 'typeahead typeahead-long dropdown-menu',
9599 // style: 'display:none'
9604 if(!this.multiple && this.showToggleBtn){
9610 if (this.caret != false) {
9613 cls: 'fa fa-' + this.caret
9620 cls : 'input-group-addon btn dropdown-toggle',
9625 cls: 'combobox-clear',
9639 combobox.cls += ' roo-select2-container-multi';
9642 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9644 // Roo.log("left and has label");
9648 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9649 tooltip : 'This field is required'
9654 cls : 'control-label col-sm-' + this.labelWidth,
9655 html : this.fieldLabel
9659 cls : "col-sm-" + (12 - this.labelWidth),
9667 if(this.indicatorpos == 'right'){
9672 cls : 'control-label col-sm-' + this.labelWidth,
9673 html : this.fieldLabel
9678 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9679 tooltip : 'This field is required'
9682 cls : "col-sm-" + (12 - this.labelWidth),
9691 } else if ( this.fieldLabel.length) {
9692 // Roo.log(" label");
9696 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9697 tooltip : 'This field is required'
9701 //cls : 'input-group-addon',
9702 html : this.fieldLabel
9710 if(this.indicatorpos == 'right'){
9715 //cls : 'input-group-addon',
9716 html : this.fieldLabel
9721 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9722 tooltip : 'This field is required'
9733 // Roo.log(" no label && no align");
9740 ['xs','sm','md','lg'].map(function(size){
9741 if (settings[size]) {
9742 cfg.cls += ' col-' + size + '-' + settings[size];
9753 onResize : function(w, h){
9754 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9755 // if(typeof w == 'number'){
9756 // var x = w - this.trigger.getWidth();
9757 // this.inputEl().setWidth(this.adjustWidth('input', x));
9758 // this.trigger.setStyle('left', x+'px');
9763 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9766 getResizeEl : function(){
9767 return this.inputEl();
9771 getPositionEl : function(){
9772 return this.inputEl();
9776 alignErrorIcon : function(){
9777 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9781 initEvents : function(){
9785 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9786 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9787 if(!this.multiple && this.showToggleBtn){
9788 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9789 if(this.hideTrigger){
9790 this.trigger.setDisplayed(false);
9792 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9796 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9799 if(this.removable && !this.editable && !this.tickable){
9800 var close = this.closeTriggerEl();
9803 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9804 close.on('click', this.removeBtnClick, this, close);
9808 //this.trigger.addClassOnOver('x-form-trigger-over');
9809 //this.trigger.addClassOnClick('x-form-trigger-click');
9812 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9816 closeTriggerEl : function()
9818 var close = this.el.select('.roo-combo-removable-btn', true).first();
9819 return close ? close : false;
9822 removeBtnClick : function(e, h, el)
9826 if(this.fireEvent("remove", this) !== false){
9828 this.fireEvent("afterremove", this)
9832 createList : function()
9834 this.list = Roo.get(document.body).createChild({
9836 cls: 'typeahead typeahead-long dropdown-menu',
9837 style: 'display:none'
9840 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9845 initTrigger : function(){
9850 onDestroy : function(){
9852 this.trigger.removeAllListeners();
9853 // this.trigger.remove();
9856 // this.wrap.remove();
9858 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9862 onFocus : function(){
9863 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9866 this.wrap.addClass('x-trigger-wrap-focus');
9867 this.mimicing = true;
9868 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9869 if(this.monitorTab){
9870 this.el.on("keydown", this.checkTab, this);
9877 checkTab : function(e){
9878 if(e.getKey() == e.TAB){
9884 onBlur : function(){
9889 mimicBlur : function(e, t){
9891 if(!this.wrap.contains(t) && this.validateBlur()){
9898 triggerBlur : function(){
9899 this.mimicing = false;
9900 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9901 if(this.monitorTab){
9902 this.el.un("keydown", this.checkTab, this);
9904 //this.wrap.removeClass('x-trigger-wrap-focus');
9905 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9909 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9910 validateBlur : function(e, t){
9915 onDisable : function(){
9916 this.inputEl().dom.disabled = true;
9917 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9919 // this.wrap.addClass('x-item-disabled');
9924 onEnable : function(){
9925 this.inputEl().dom.disabled = false;
9926 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9928 // this.el.removeClass('x-item-disabled');
9933 onShow : function(){
9934 var ae = this.getActionEl();
9937 ae.dom.style.display = '';
9938 ae.dom.style.visibility = 'visible';
9944 onHide : function(){
9945 var ae = this.getActionEl();
9946 ae.dom.style.display = 'none';
9950 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9951 * by an implementing function.
9953 * @param {EventObject} e
9955 onTriggerClick : Roo.emptyFn
9959 * Ext JS Library 1.1.1
9960 * Copyright(c) 2006-2007, Ext JS, LLC.
9962 * Originally Released Under LGPL - original licence link has changed is not relivant.
9965 * <script type="text/javascript">
9970 * @class Roo.data.SortTypes
9972 * Defines the default sorting (casting?) comparison functions used when sorting data.
9974 Roo.data.SortTypes = {
9976 * Default sort that does nothing
9977 * @param {Mixed} s The value being converted
9978 * @return {Mixed} The comparison value
9985 * The regular expression used to strip tags
9989 stripTagsRE : /<\/?[^>]+>/gi,
9992 * Strips all HTML tags to sort on text only
9993 * @param {Mixed} s The value being converted
9994 * @return {String} The comparison value
9996 asText : function(s){
9997 return String(s).replace(this.stripTagsRE, "");
10001 * Strips all HTML tags to sort on text only - Case insensitive
10002 * @param {Mixed} s The value being converted
10003 * @return {String} The comparison value
10005 asUCText : function(s){
10006 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10010 * Case insensitive string
10011 * @param {Mixed} s The value being converted
10012 * @return {String} The comparison value
10014 asUCString : function(s) {
10015 return String(s).toUpperCase();
10020 * @param {Mixed} s The value being converted
10021 * @return {Number} The comparison value
10023 asDate : function(s) {
10027 if(s instanceof Date){
10028 return s.getTime();
10030 return Date.parse(String(s));
10035 * @param {Mixed} s The value being converted
10036 * @return {Float} The comparison value
10038 asFloat : function(s) {
10039 var val = parseFloat(String(s).replace(/,/g, ""));
10048 * @param {Mixed} s The value being converted
10049 * @return {Number} The comparison value
10051 asInt : function(s) {
10052 var val = parseInt(String(s).replace(/,/g, ""));
10060 * Ext JS Library 1.1.1
10061 * Copyright(c) 2006-2007, Ext JS, LLC.
10063 * Originally Released Under LGPL - original licence link has changed is not relivant.
10066 * <script type="text/javascript">
10070 * @class Roo.data.Record
10071 * Instances of this class encapsulate both record <em>definition</em> information, and record
10072 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10073 * to access Records cached in an {@link Roo.data.Store} object.<br>
10075 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10076 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10079 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10081 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10082 * {@link #create}. The parameters are the same.
10083 * @param {Array} data An associative Array of data values keyed by the field name.
10084 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10085 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10086 * not specified an integer id is generated.
10088 Roo.data.Record = function(data, id){
10089 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10094 * Generate a constructor for a specific record layout.
10095 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10096 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10097 * Each field definition object may contain the following properties: <ul>
10098 * <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,
10099 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10100 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10101 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10102 * is being used, then this is a string containing the javascript expression to reference the data relative to
10103 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10104 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10105 * this may be omitted.</p></li>
10106 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10107 * <ul><li>auto (Default, implies no conversion)</li>
10112 * <li>date</li></ul></p></li>
10113 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10114 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10115 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10116 * by the Reader into an object that will be stored in the Record. It is passed the
10117 * following parameters:<ul>
10118 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10120 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10122 * <br>usage:<br><pre><code>
10123 var TopicRecord = Roo.data.Record.create(
10124 {name: 'title', mapping: 'topic_title'},
10125 {name: 'author', mapping: 'username'},
10126 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10127 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10128 {name: 'lastPoster', mapping: 'user2'},
10129 {name: 'excerpt', mapping: 'post_text'}
10132 var myNewRecord = new TopicRecord({
10133 title: 'Do my job please',
10136 lastPost: new Date(),
10137 lastPoster: 'Animal',
10138 excerpt: 'No way dude!'
10140 myStore.add(myNewRecord);
10145 Roo.data.Record.create = function(o){
10146 var f = function(){
10147 f.superclass.constructor.apply(this, arguments);
10149 Roo.extend(f, Roo.data.Record);
10150 var p = f.prototype;
10151 p.fields = new Roo.util.MixedCollection(false, function(field){
10154 for(var i = 0, len = o.length; i < len; i++){
10155 p.fields.add(new Roo.data.Field(o[i]));
10157 f.getField = function(name){
10158 return p.fields.get(name);
10163 Roo.data.Record.AUTO_ID = 1000;
10164 Roo.data.Record.EDIT = 'edit';
10165 Roo.data.Record.REJECT = 'reject';
10166 Roo.data.Record.COMMIT = 'commit';
10168 Roo.data.Record.prototype = {
10170 * Readonly flag - true if this record has been modified.
10179 join : function(store){
10180 this.store = store;
10184 * Set the named field to the specified value.
10185 * @param {String} name The name of the field to set.
10186 * @param {Object} value The value to set the field to.
10188 set : function(name, value){
10189 if(this.data[name] == value){
10193 if(!this.modified){
10194 this.modified = {};
10196 if(typeof this.modified[name] == 'undefined'){
10197 this.modified[name] = this.data[name];
10199 this.data[name] = value;
10200 if(!this.editing && this.store){
10201 this.store.afterEdit(this);
10206 * Get the value of the named field.
10207 * @param {String} name The name of the field to get the value of.
10208 * @return {Object} The value of the field.
10210 get : function(name){
10211 return this.data[name];
10215 beginEdit : function(){
10216 this.editing = true;
10217 this.modified = {};
10221 cancelEdit : function(){
10222 this.editing = false;
10223 delete this.modified;
10227 endEdit : function(){
10228 this.editing = false;
10229 if(this.dirty && this.store){
10230 this.store.afterEdit(this);
10235 * Usually called by the {@link Roo.data.Store} which owns the Record.
10236 * Rejects all changes made to the Record since either creation, or the last commit operation.
10237 * Modified fields are reverted to their original values.
10239 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10240 * of reject operations.
10242 reject : function(){
10243 var m = this.modified;
10245 if(typeof m[n] != "function"){
10246 this.data[n] = m[n];
10249 this.dirty = false;
10250 delete this.modified;
10251 this.editing = false;
10253 this.store.afterReject(this);
10258 * Usually called by the {@link Roo.data.Store} which owns the Record.
10259 * Commits all changes made to the Record since either creation, or the last commit operation.
10261 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10262 * of commit operations.
10264 commit : function(){
10265 this.dirty = false;
10266 delete this.modified;
10267 this.editing = false;
10269 this.store.afterCommit(this);
10274 hasError : function(){
10275 return this.error != null;
10279 clearError : function(){
10284 * Creates a copy of this record.
10285 * @param {String} id (optional) A new record id if you don't want to use this record's id
10288 copy : function(newId) {
10289 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10293 * Ext JS Library 1.1.1
10294 * Copyright(c) 2006-2007, Ext JS, LLC.
10296 * Originally Released Under LGPL - original licence link has changed is not relivant.
10299 * <script type="text/javascript">
10305 * @class Roo.data.Store
10306 * @extends Roo.util.Observable
10307 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10308 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10310 * 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
10311 * has no knowledge of the format of the data returned by the Proxy.<br>
10313 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10314 * instances from the data object. These records are cached and made available through accessor functions.
10316 * Creates a new Store.
10317 * @param {Object} config A config object containing the objects needed for the Store to access data,
10318 * and read the data into Records.
10320 Roo.data.Store = function(config){
10321 this.data = new Roo.util.MixedCollection(false);
10322 this.data.getKey = function(o){
10325 this.baseParams = {};
10327 this.paramNames = {
10332 "multisort" : "_multisort"
10335 if(config && config.data){
10336 this.inlineData = config.data;
10337 delete config.data;
10340 Roo.apply(this, config);
10342 if(this.reader){ // reader passed
10343 this.reader = Roo.factory(this.reader, Roo.data);
10344 this.reader.xmodule = this.xmodule || false;
10345 if(!this.recordType){
10346 this.recordType = this.reader.recordType;
10348 if(this.reader.onMetaChange){
10349 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10353 if(this.recordType){
10354 this.fields = this.recordType.prototype.fields;
10356 this.modified = [];
10360 * @event datachanged
10361 * Fires when the data cache has changed, and a widget which is using this Store
10362 * as a Record cache should refresh its view.
10363 * @param {Store} this
10365 datachanged : true,
10367 * @event metachange
10368 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10369 * @param {Store} this
10370 * @param {Object} meta The JSON metadata
10375 * Fires when Records have been added to the Store
10376 * @param {Store} this
10377 * @param {Roo.data.Record[]} records The array of Records added
10378 * @param {Number} index The index at which the record(s) were added
10383 * Fires when a Record has been removed from the Store
10384 * @param {Store} this
10385 * @param {Roo.data.Record} record The Record that was removed
10386 * @param {Number} index The index at which the record was removed
10391 * Fires when a Record has been updated
10392 * @param {Store} this
10393 * @param {Roo.data.Record} record The Record that was updated
10394 * @param {String} operation The update operation being performed. Value may be one of:
10396 Roo.data.Record.EDIT
10397 Roo.data.Record.REJECT
10398 Roo.data.Record.COMMIT
10404 * Fires when the data cache has been cleared.
10405 * @param {Store} this
10409 * @event beforeload
10410 * Fires before a request is made for a new data object. If the beforeload handler returns false
10411 * the load action will be canceled.
10412 * @param {Store} this
10413 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10417 * @event beforeloadadd
10418 * Fires after a new set of Records has been loaded.
10419 * @param {Store} this
10420 * @param {Roo.data.Record[]} records The Records that were loaded
10421 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10423 beforeloadadd : true,
10426 * Fires after a new set of Records has been loaded, before they are added to the store.
10427 * @param {Store} this
10428 * @param {Roo.data.Record[]} records The Records that were loaded
10429 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10430 * @params {Object} return from reader
10434 * @event loadexception
10435 * Fires if an exception occurs in the Proxy during loading.
10436 * Called with the signature of the Proxy's "loadexception" event.
10437 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10440 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10441 * @param {Object} load options
10442 * @param {Object} jsonData from your request (normally this contains the Exception)
10444 loadexception : true
10448 this.proxy = Roo.factory(this.proxy, Roo.data);
10449 this.proxy.xmodule = this.xmodule || false;
10450 this.relayEvents(this.proxy, ["loadexception"]);
10452 this.sortToggle = {};
10453 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10455 Roo.data.Store.superclass.constructor.call(this);
10457 if(this.inlineData){
10458 this.loadData(this.inlineData);
10459 delete this.inlineData;
10463 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10465 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10466 * without a remote query - used by combo/forms at present.
10470 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10473 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10476 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10477 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10480 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10481 * on any HTTP request
10484 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10487 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10491 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10492 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10494 remoteSort : false,
10497 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10498 * loaded or when a record is removed. (defaults to false).
10500 pruneModifiedRecords : false,
10503 lastOptions : null,
10506 * Add Records to the Store and fires the add event.
10507 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10509 add : function(records){
10510 records = [].concat(records);
10511 for(var i = 0, len = records.length; i < len; i++){
10512 records[i].join(this);
10514 var index = this.data.length;
10515 this.data.addAll(records);
10516 this.fireEvent("add", this, records, index);
10520 * Remove a Record from the Store and fires the remove event.
10521 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10523 remove : function(record){
10524 var index = this.data.indexOf(record);
10525 this.data.removeAt(index);
10526 if(this.pruneModifiedRecords){
10527 this.modified.remove(record);
10529 this.fireEvent("remove", this, record, index);
10533 * Remove all Records from the Store and fires the clear event.
10535 removeAll : function(){
10537 if(this.pruneModifiedRecords){
10538 this.modified = [];
10540 this.fireEvent("clear", this);
10544 * Inserts Records to the Store at the given index and fires the add event.
10545 * @param {Number} index The start index at which to insert the passed Records.
10546 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10548 insert : function(index, records){
10549 records = [].concat(records);
10550 for(var i = 0, len = records.length; i < len; i++){
10551 this.data.insert(index, records[i]);
10552 records[i].join(this);
10554 this.fireEvent("add", this, records, index);
10558 * Get the index within the cache of the passed Record.
10559 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10560 * @return {Number} The index of the passed Record. Returns -1 if not found.
10562 indexOf : function(record){
10563 return this.data.indexOf(record);
10567 * Get the index within the cache of the Record with the passed id.
10568 * @param {String} id The id of the Record to find.
10569 * @return {Number} The index of the Record. Returns -1 if not found.
10571 indexOfId : function(id){
10572 return this.data.indexOfKey(id);
10576 * Get the Record with the specified id.
10577 * @param {String} id The id of the Record to find.
10578 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10580 getById : function(id){
10581 return this.data.key(id);
10585 * Get the Record at the specified index.
10586 * @param {Number} index The index of the Record to find.
10587 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10589 getAt : function(index){
10590 return this.data.itemAt(index);
10594 * Returns a range of Records between specified indices.
10595 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10596 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10597 * @return {Roo.data.Record[]} An array of Records
10599 getRange : function(start, end){
10600 return this.data.getRange(start, end);
10604 storeOptions : function(o){
10605 o = Roo.apply({}, o);
10608 this.lastOptions = o;
10612 * Loads the Record cache from the configured Proxy using the configured Reader.
10614 * If using remote paging, then the first load call must specify the <em>start</em>
10615 * and <em>limit</em> properties in the options.params property to establish the initial
10616 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10618 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10619 * and this call will return before the new data has been loaded. Perform any post-processing
10620 * in a callback function, or in a "load" event handler.</strong>
10622 * @param {Object} options An object containing properties which control loading options:<ul>
10623 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10624 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10625 * passed the following arguments:<ul>
10626 * <li>r : Roo.data.Record[]</li>
10627 * <li>options: Options object from the load call</li>
10628 * <li>success: Boolean success indicator</li></ul></li>
10629 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10630 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10633 load : function(options){
10634 options = options || {};
10635 if(this.fireEvent("beforeload", this, options) !== false){
10636 this.storeOptions(options);
10637 var p = Roo.apply(options.params || {}, this.baseParams);
10638 // if meta was not loaded from remote source.. try requesting it.
10639 if (!this.reader.metaFromRemote) {
10640 p._requestMeta = 1;
10642 if(this.sortInfo && this.remoteSort){
10643 var pn = this.paramNames;
10644 p[pn["sort"]] = this.sortInfo.field;
10645 p[pn["dir"]] = this.sortInfo.direction;
10647 if (this.multiSort) {
10648 var pn = this.paramNames;
10649 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10652 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10657 * Reloads the Record cache from the configured Proxy using the configured Reader and
10658 * the options from the last load operation performed.
10659 * @param {Object} options (optional) An object containing properties which may override the options
10660 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10661 * the most recently used options are reused).
10663 reload : function(options){
10664 this.load(Roo.applyIf(options||{}, this.lastOptions));
10668 // Called as a callback by the Reader during a load operation.
10669 loadRecords : function(o, options, success){
10670 if(!o || success === false){
10671 if(success !== false){
10672 this.fireEvent("load", this, [], options, o);
10674 if(options.callback){
10675 options.callback.call(options.scope || this, [], options, false);
10679 // if data returned failure - throw an exception.
10680 if (o.success === false) {
10681 // show a message if no listener is registered.
10682 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10683 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10685 // loadmask wil be hooked into this..
10686 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10689 var r = o.records, t = o.totalRecords || r.length;
10691 this.fireEvent("beforeloadadd", this, r, options, o);
10693 if(!options || options.add !== true){
10694 if(this.pruneModifiedRecords){
10695 this.modified = [];
10697 for(var i = 0, len = r.length; i < len; i++){
10701 this.data = this.snapshot;
10702 delete this.snapshot;
10705 this.data.addAll(r);
10706 this.totalLength = t;
10708 this.fireEvent("datachanged", this);
10710 this.totalLength = Math.max(t, this.data.length+r.length);
10713 this.fireEvent("load", this, r, options, o);
10714 if(options.callback){
10715 options.callback.call(options.scope || this, r, options, true);
10721 * Loads data from a passed data block. A Reader which understands the format of the data
10722 * must have been configured in the constructor.
10723 * @param {Object} data The data block from which to read the Records. The format of the data expected
10724 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10725 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10727 loadData : function(o, append){
10728 var r = this.reader.readRecords(o);
10729 this.loadRecords(r, {add: append}, true);
10733 * Gets the number of cached records.
10735 * <em>If using paging, this may not be the total size of the dataset. If the data object
10736 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10737 * the data set size</em>
10739 getCount : function(){
10740 return this.data.length || 0;
10744 * Gets the total number of records in the dataset as returned by the server.
10746 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10747 * the dataset size</em>
10749 getTotalCount : function(){
10750 return this.totalLength || 0;
10754 * Returns the sort state of the Store as an object with two properties:
10756 field {String} The name of the field by which the Records are sorted
10757 direction {String} The sort order, "ASC" or "DESC"
10760 getSortState : function(){
10761 return this.sortInfo;
10765 applySort : function(){
10766 if(this.sortInfo && !this.remoteSort){
10767 var s = this.sortInfo, f = s.field;
10768 var st = this.fields.get(f).sortType;
10769 var fn = function(r1, r2){
10770 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10771 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10773 this.data.sort(s.direction, fn);
10774 if(this.snapshot && this.snapshot != this.data){
10775 this.snapshot.sort(s.direction, fn);
10781 * Sets the default sort column and order to be used by the next load operation.
10782 * @param {String} fieldName The name of the field to sort by.
10783 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10785 setDefaultSort : function(field, dir){
10786 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10790 * Sort the Records.
10791 * If remote sorting is used, the sort is performed on the server, and the cache is
10792 * reloaded. If local sorting is used, the cache is sorted internally.
10793 * @param {String} fieldName The name of the field to sort by.
10794 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10796 sort : function(fieldName, dir){
10797 var f = this.fields.get(fieldName);
10799 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10801 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10802 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10807 this.sortToggle[f.name] = dir;
10808 this.sortInfo = {field: f.name, direction: dir};
10809 if(!this.remoteSort){
10811 this.fireEvent("datachanged", this);
10813 this.load(this.lastOptions);
10818 * Calls the specified function for each of the Records in the cache.
10819 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10820 * Returning <em>false</em> aborts and exits the iteration.
10821 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10823 each : function(fn, scope){
10824 this.data.each(fn, scope);
10828 * Gets all records modified since the last commit. Modified records are persisted across load operations
10829 * (e.g., during paging).
10830 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10832 getModifiedRecords : function(){
10833 return this.modified;
10837 createFilterFn : function(property, value, anyMatch){
10838 if(!value.exec){ // not a regex
10839 value = String(value);
10840 if(value.length == 0){
10843 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10845 return function(r){
10846 return value.test(r.data[property]);
10851 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10852 * @param {String} property A field on your records
10853 * @param {Number} start The record index to start at (defaults to 0)
10854 * @param {Number} end The last record index to include (defaults to length - 1)
10855 * @return {Number} The sum
10857 sum : function(property, start, end){
10858 var rs = this.data.items, v = 0;
10859 start = start || 0;
10860 end = (end || end === 0) ? end : rs.length-1;
10862 for(var i = start; i <= end; i++){
10863 v += (rs[i].data[property] || 0);
10869 * Filter the records by a specified property.
10870 * @param {String} field A field on your records
10871 * @param {String/RegExp} value Either a string that the field
10872 * should start with or a RegExp to test against the field
10873 * @param {Boolean} anyMatch True to match any part not just the beginning
10875 filter : function(property, value, anyMatch){
10876 var fn = this.createFilterFn(property, value, anyMatch);
10877 return fn ? this.filterBy(fn) : this.clearFilter();
10881 * Filter by a function. The specified function will be called with each
10882 * record in this data source. If the function returns true the record is included,
10883 * otherwise it is filtered.
10884 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10885 * @param {Object} scope (optional) The scope of the function (defaults to this)
10887 filterBy : function(fn, scope){
10888 this.snapshot = this.snapshot || this.data;
10889 this.data = this.queryBy(fn, scope||this);
10890 this.fireEvent("datachanged", this);
10894 * Query the records by a specified property.
10895 * @param {String} field A field on your records
10896 * @param {String/RegExp} value Either a string that the field
10897 * should start with or a RegExp to test against the field
10898 * @param {Boolean} anyMatch True to match any part not just the beginning
10899 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10901 query : function(property, value, anyMatch){
10902 var fn = this.createFilterFn(property, value, anyMatch);
10903 return fn ? this.queryBy(fn) : this.data.clone();
10907 * Query by a function. The specified function will be called with each
10908 * record in this data source. If the function returns true the record is included
10910 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10911 * @param {Object} scope (optional) The scope of the function (defaults to this)
10912 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10914 queryBy : function(fn, scope){
10915 var data = this.snapshot || this.data;
10916 return data.filterBy(fn, scope||this);
10920 * Collects unique values for a particular dataIndex from this store.
10921 * @param {String} dataIndex The property to collect
10922 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10923 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10924 * @return {Array} An array of the unique values
10926 collect : function(dataIndex, allowNull, bypassFilter){
10927 var d = (bypassFilter === true && this.snapshot) ?
10928 this.snapshot.items : this.data.items;
10929 var v, sv, r = [], l = {};
10930 for(var i = 0, len = d.length; i < len; i++){
10931 v = d[i].data[dataIndex];
10933 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10942 * Revert to a view of the Record cache with no filtering applied.
10943 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10945 clearFilter : function(suppressEvent){
10946 if(this.snapshot && this.snapshot != this.data){
10947 this.data = this.snapshot;
10948 delete this.snapshot;
10949 if(suppressEvent !== true){
10950 this.fireEvent("datachanged", this);
10956 afterEdit : function(record){
10957 if(this.modified.indexOf(record) == -1){
10958 this.modified.push(record);
10960 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10964 afterReject : function(record){
10965 this.modified.remove(record);
10966 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10970 afterCommit : function(record){
10971 this.modified.remove(record);
10972 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10976 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10977 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10979 commitChanges : function(){
10980 var m = this.modified.slice(0);
10981 this.modified = [];
10982 for(var i = 0, len = m.length; i < len; i++){
10988 * Cancel outstanding changes on all changed records.
10990 rejectChanges : function(){
10991 var m = this.modified.slice(0);
10992 this.modified = [];
10993 for(var i = 0, len = m.length; i < len; i++){
10998 onMetaChange : function(meta, rtype, o){
10999 this.recordType = rtype;
11000 this.fields = rtype.prototype.fields;
11001 delete this.snapshot;
11002 this.sortInfo = meta.sortInfo || this.sortInfo;
11003 this.modified = [];
11004 this.fireEvent('metachange', this, this.reader.meta);
11007 moveIndex : function(data, type)
11009 var index = this.indexOf(data);
11011 var newIndex = index + type;
11015 this.insert(newIndex, data);
11020 * Ext JS Library 1.1.1
11021 * Copyright(c) 2006-2007, Ext JS, LLC.
11023 * Originally Released Under LGPL - original licence link has changed is not relivant.
11026 * <script type="text/javascript">
11030 * @class Roo.data.SimpleStore
11031 * @extends Roo.data.Store
11032 * Small helper class to make creating Stores from Array data easier.
11033 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11034 * @cfg {Array} fields An array of field definition objects, or field name strings.
11035 * @cfg {Array} data The multi-dimensional array of data
11037 * @param {Object} config
11039 Roo.data.SimpleStore = function(config){
11040 Roo.data.SimpleStore.superclass.constructor.call(this, {
11042 reader: new Roo.data.ArrayReader({
11045 Roo.data.Record.create(config.fields)
11047 proxy : new Roo.data.MemoryProxy(config.data)
11051 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11053 * Ext JS Library 1.1.1
11054 * Copyright(c) 2006-2007, Ext JS, LLC.
11056 * Originally Released Under LGPL - original licence link has changed is not relivant.
11059 * <script type="text/javascript">
11064 * @extends Roo.data.Store
11065 * @class Roo.data.JsonStore
11066 * Small helper class to make creating Stores for JSON data easier. <br/>
11068 var store = new Roo.data.JsonStore({
11069 url: 'get-images.php',
11071 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11074 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11075 * JsonReader and HttpProxy (unless inline data is provided).</b>
11076 * @cfg {Array} fields An array of field definition objects, or field name strings.
11078 * @param {Object} config
11080 Roo.data.JsonStore = function(c){
11081 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11082 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11083 reader: new Roo.data.JsonReader(c, c.fields)
11086 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11088 * Ext JS Library 1.1.1
11089 * Copyright(c) 2006-2007, Ext JS, LLC.
11091 * Originally Released Under LGPL - original licence link has changed is not relivant.
11094 * <script type="text/javascript">
11098 Roo.data.Field = function(config){
11099 if(typeof config == "string"){
11100 config = {name: config};
11102 Roo.apply(this, config);
11105 this.type = "auto";
11108 var st = Roo.data.SortTypes;
11109 // named sortTypes are supported, here we look them up
11110 if(typeof this.sortType == "string"){
11111 this.sortType = st[this.sortType];
11114 // set default sortType for strings and dates
11115 if(!this.sortType){
11118 this.sortType = st.asUCString;
11121 this.sortType = st.asDate;
11124 this.sortType = st.none;
11129 var stripRe = /[\$,%]/g;
11131 // prebuilt conversion function for this field, instead of
11132 // switching every time we're reading a value
11134 var cv, dateFormat = this.dateFormat;
11139 cv = function(v){ return v; };
11142 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11146 return v !== undefined && v !== null && v !== '' ?
11147 parseInt(String(v).replace(stripRe, ""), 10) : '';
11152 return v !== undefined && v !== null && v !== '' ?
11153 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11158 cv = function(v){ return v === true || v === "true" || v == 1; };
11165 if(v instanceof Date){
11169 if(dateFormat == "timestamp"){
11170 return new Date(v*1000);
11172 return Date.parseDate(v, dateFormat);
11174 var parsed = Date.parse(v);
11175 return parsed ? new Date(parsed) : null;
11184 Roo.data.Field.prototype = {
11192 * Ext JS Library 1.1.1
11193 * Copyright(c) 2006-2007, Ext JS, LLC.
11195 * Originally Released Under LGPL - original licence link has changed is not relivant.
11198 * <script type="text/javascript">
11201 // Base class for reading structured data from a data source. This class is intended to be
11202 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11205 * @class Roo.data.DataReader
11206 * Base class for reading structured data from a data source. This class is intended to be
11207 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11210 Roo.data.DataReader = function(meta, recordType){
11214 this.recordType = recordType instanceof Array ?
11215 Roo.data.Record.create(recordType) : recordType;
11218 Roo.data.DataReader.prototype = {
11220 * Create an empty record
11221 * @param {Object} data (optional) - overlay some values
11222 * @return {Roo.data.Record} record created.
11224 newRow : function(d) {
11226 this.recordType.prototype.fields.each(function(c) {
11228 case 'int' : da[c.name] = 0; break;
11229 case 'date' : da[c.name] = new Date(); break;
11230 case 'float' : da[c.name] = 0.0; break;
11231 case 'boolean' : da[c.name] = false; break;
11232 default : da[c.name] = ""; break;
11236 return new this.recordType(Roo.apply(da, d));
11241 * Ext JS Library 1.1.1
11242 * Copyright(c) 2006-2007, Ext JS, LLC.
11244 * Originally Released Under LGPL - original licence link has changed is not relivant.
11247 * <script type="text/javascript">
11251 * @class Roo.data.DataProxy
11252 * @extends Roo.data.Observable
11253 * This class is an abstract base class for implementations which provide retrieval of
11254 * unformatted data objects.<br>
11256 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11257 * (of the appropriate type which knows how to parse the data object) to provide a block of
11258 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11260 * Custom implementations must implement the load method as described in
11261 * {@link Roo.data.HttpProxy#load}.
11263 Roo.data.DataProxy = function(){
11266 * @event beforeload
11267 * Fires before a network request is made to retrieve a data object.
11268 * @param {Object} This DataProxy object.
11269 * @param {Object} params The params parameter to the load function.
11274 * Fires before the load method's callback is called.
11275 * @param {Object} This DataProxy object.
11276 * @param {Object} o The data object.
11277 * @param {Object} arg The callback argument object passed to the load function.
11281 * @event loadexception
11282 * Fires if an Exception occurs during data retrieval.
11283 * @param {Object} This DataProxy object.
11284 * @param {Object} o The data object.
11285 * @param {Object} arg The callback argument object passed to the load function.
11286 * @param {Object} e The Exception.
11288 loadexception : true
11290 Roo.data.DataProxy.superclass.constructor.call(this);
11293 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11296 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11300 * Ext JS Library 1.1.1
11301 * Copyright(c) 2006-2007, Ext JS, LLC.
11303 * Originally Released Under LGPL - original licence link has changed is not relivant.
11306 * <script type="text/javascript">
11309 * @class Roo.data.MemoryProxy
11310 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11311 * to the Reader when its load method is called.
11313 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11315 Roo.data.MemoryProxy = function(data){
11319 Roo.data.MemoryProxy.superclass.constructor.call(this);
11323 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11326 * Load data from the requested source (in this case an in-memory
11327 * data object passed to the constructor), read the data object into
11328 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11329 * process that block using the passed callback.
11330 * @param {Object} params This parameter is not used by the MemoryProxy class.
11331 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11332 * object into a block of Roo.data.Records.
11333 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11334 * The function must be passed <ul>
11335 * <li>The Record block object</li>
11336 * <li>The "arg" argument from the load function</li>
11337 * <li>A boolean success indicator</li>
11339 * @param {Object} scope The scope in which to call the callback
11340 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11342 load : function(params, reader, callback, scope, arg){
11343 params = params || {};
11346 result = reader.readRecords(this.data);
11348 this.fireEvent("loadexception", this, arg, null, e);
11349 callback.call(scope, null, arg, false);
11352 callback.call(scope, result, arg, true);
11356 update : function(params, records){
11361 * Ext JS Library 1.1.1
11362 * Copyright(c) 2006-2007, Ext JS, LLC.
11364 * Originally Released Under LGPL - original licence link has changed is not relivant.
11367 * <script type="text/javascript">
11370 * @class Roo.data.HttpProxy
11371 * @extends Roo.data.DataProxy
11372 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11373 * configured to reference a certain URL.<br><br>
11375 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11376 * from which the running page was served.<br><br>
11378 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11380 * Be aware that to enable the browser to parse an XML document, the server must set
11381 * the Content-Type header in the HTTP response to "text/xml".
11383 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11384 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11385 * will be used to make the request.
11387 Roo.data.HttpProxy = function(conn){
11388 Roo.data.HttpProxy.superclass.constructor.call(this);
11389 // is conn a conn config or a real conn?
11391 this.useAjax = !conn || !conn.events;
11395 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11396 // thse are take from connection...
11399 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11402 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11403 * extra parameters to each request made by this object. (defaults to undefined)
11406 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11407 * to each request made by this object. (defaults to undefined)
11410 * @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)
11413 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11416 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11422 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11426 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11427 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11428 * a finer-grained basis than the DataProxy events.
11430 getConnection : function(){
11431 return this.useAjax ? Roo.Ajax : this.conn;
11435 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11436 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11437 * process that block using the passed callback.
11438 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11439 * for the request to the remote server.
11440 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11441 * object into a block of Roo.data.Records.
11442 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11443 * The function must be passed <ul>
11444 * <li>The Record block object</li>
11445 * <li>The "arg" argument from the load function</li>
11446 * <li>A boolean success indicator</li>
11448 * @param {Object} scope The scope in which to call the callback
11449 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11451 load : function(params, reader, callback, scope, arg){
11452 if(this.fireEvent("beforeload", this, params) !== false){
11454 params : params || {},
11456 callback : callback,
11461 callback : this.loadResponse,
11465 Roo.applyIf(o, this.conn);
11466 if(this.activeRequest){
11467 Roo.Ajax.abort(this.activeRequest);
11469 this.activeRequest = Roo.Ajax.request(o);
11471 this.conn.request(o);
11474 callback.call(scope||this, null, arg, false);
11479 loadResponse : function(o, success, response){
11480 delete this.activeRequest;
11482 this.fireEvent("loadexception", this, o, response);
11483 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11488 result = o.reader.read(response);
11490 this.fireEvent("loadexception", this, o, response, e);
11491 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11495 this.fireEvent("load", this, o, o.request.arg);
11496 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11500 update : function(dataSet){
11505 updateResponse : function(dataSet){
11510 * Ext JS Library 1.1.1
11511 * Copyright(c) 2006-2007, Ext JS, LLC.
11513 * Originally Released Under LGPL - original licence link has changed is not relivant.
11516 * <script type="text/javascript">
11520 * @class Roo.data.ScriptTagProxy
11521 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11522 * other than the originating domain of the running page.<br><br>
11524 * <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
11525 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11527 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11528 * source code that is used as the source inside a <script> tag.<br><br>
11530 * In order for the browser to process the returned data, the server must wrap the data object
11531 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11532 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11533 * depending on whether the callback name was passed:
11536 boolean scriptTag = false;
11537 String cb = request.getParameter("callback");
11540 response.setContentType("text/javascript");
11542 response.setContentType("application/x-json");
11544 Writer out = response.getWriter();
11546 out.write(cb + "(");
11548 out.print(dataBlock.toJsonString());
11555 * @param {Object} config A configuration object.
11557 Roo.data.ScriptTagProxy = function(config){
11558 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11559 Roo.apply(this, config);
11560 this.head = document.getElementsByTagName("head")[0];
11563 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11565 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11567 * @cfg {String} url The URL from which to request the data object.
11570 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11574 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11575 * the server the name of the callback function set up by the load call to process the returned data object.
11576 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11577 * javascript output which calls this named function passing the data object as its only parameter.
11579 callbackParam : "callback",
11581 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11582 * name to the request.
11587 * Load data from the configured URL, read the data object into
11588 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11589 * process that block using the passed callback.
11590 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11591 * for the request to the remote server.
11592 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11593 * object into a block of Roo.data.Records.
11594 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11595 * The function must be passed <ul>
11596 * <li>The Record block object</li>
11597 * <li>The "arg" argument from the load function</li>
11598 * <li>A boolean success indicator</li>
11600 * @param {Object} scope The scope in which to call the callback
11601 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11603 load : function(params, reader, callback, scope, arg){
11604 if(this.fireEvent("beforeload", this, params) !== false){
11606 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11608 var url = this.url;
11609 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11611 url += "&_dc=" + (new Date().getTime());
11613 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11616 cb : "stcCallback"+transId,
11617 scriptId : "stcScript"+transId,
11621 callback : callback,
11627 window[trans.cb] = function(o){
11628 conn.handleResponse(o, trans);
11631 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11633 if(this.autoAbort !== false){
11637 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11639 var script = document.createElement("script");
11640 script.setAttribute("src", url);
11641 script.setAttribute("type", "text/javascript");
11642 script.setAttribute("id", trans.scriptId);
11643 this.head.appendChild(script);
11645 this.trans = trans;
11647 callback.call(scope||this, null, arg, false);
11652 isLoading : function(){
11653 return this.trans ? true : false;
11657 * Abort the current server request.
11659 abort : function(){
11660 if(this.isLoading()){
11661 this.destroyTrans(this.trans);
11666 destroyTrans : function(trans, isLoaded){
11667 this.head.removeChild(document.getElementById(trans.scriptId));
11668 clearTimeout(trans.timeoutId);
11670 window[trans.cb] = undefined;
11672 delete window[trans.cb];
11675 // if hasn't been loaded, wait for load to remove it to prevent script error
11676 window[trans.cb] = function(){
11677 window[trans.cb] = undefined;
11679 delete window[trans.cb];
11686 handleResponse : function(o, trans){
11687 this.trans = false;
11688 this.destroyTrans(trans, true);
11691 result = trans.reader.readRecords(o);
11693 this.fireEvent("loadexception", this, o, trans.arg, e);
11694 trans.callback.call(trans.scope||window, null, trans.arg, false);
11697 this.fireEvent("load", this, o, trans.arg);
11698 trans.callback.call(trans.scope||window, result, trans.arg, true);
11702 handleFailure : function(trans){
11703 this.trans = false;
11704 this.destroyTrans(trans, false);
11705 this.fireEvent("loadexception", this, null, trans.arg);
11706 trans.callback.call(trans.scope||window, null, trans.arg, false);
11710 * Ext JS Library 1.1.1
11711 * Copyright(c) 2006-2007, Ext JS, LLC.
11713 * Originally Released Under LGPL - original licence link has changed is not relivant.
11716 * <script type="text/javascript">
11720 * @class Roo.data.JsonReader
11721 * @extends Roo.data.DataReader
11722 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11723 * based on mappings in a provided Roo.data.Record constructor.
11725 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11726 * in the reply previously.
11731 var RecordDef = Roo.data.Record.create([
11732 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11733 {name: 'occupation'} // This field will use "occupation" as the mapping.
11735 var myReader = new Roo.data.JsonReader({
11736 totalProperty: "results", // The property which contains the total dataset size (optional)
11737 root: "rows", // The property which contains an Array of row objects
11738 id: "id" // The property within each row object that provides an ID for the record (optional)
11742 * This would consume a JSON file like this:
11744 { 'results': 2, 'rows': [
11745 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11746 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11749 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11750 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11751 * paged from the remote server.
11752 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11753 * @cfg {String} root name of the property which contains the Array of row objects.
11754 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11755 * @cfg {Array} fields Array of field definition objects
11757 * Create a new JsonReader
11758 * @param {Object} meta Metadata configuration options
11759 * @param {Object} recordType Either an Array of field definition objects,
11760 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11762 Roo.data.JsonReader = function(meta, recordType){
11765 // set some defaults:
11766 Roo.applyIf(meta, {
11767 totalProperty: 'total',
11768 successProperty : 'success',
11773 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11775 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11778 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11779 * Used by Store query builder to append _requestMeta to params.
11782 metaFromRemote : false,
11784 * This method is only used by a DataProxy which has retrieved data from a remote server.
11785 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11786 * @return {Object} data A data block which is used by an Roo.data.Store object as
11787 * a cache of Roo.data.Records.
11789 read : function(response){
11790 var json = response.responseText;
11792 var o = /* eval:var:o */ eval("("+json+")");
11794 throw {message: "JsonReader.read: Json object not found"};
11800 this.metaFromRemote = true;
11801 this.meta = o.metaData;
11802 this.recordType = Roo.data.Record.create(o.metaData.fields);
11803 this.onMetaChange(this.meta, this.recordType, o);
11805 return this.readRecords(o);
11808 // private function a store will implement
11809 onMetaChange : function(meta, recordType, o){
11816 simpleAccess: function(obj, subsc) {
11823 getJsonAccessor: function(){
11825 return function(expr) {
11827 return(re.test(expr))
11828 ? new Function("obj", "return obj." + expr)
11833 return Roo.emptyFn;
11838 * Create a data block containing Roo.data.Records from an XML document.
11839 * @param {Object} o An object which contains an Array of row objects in the property specified
11840 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11841 * which contains the total size of the dataset.
11842 * @return {Object} data A data block which is used by an Roo.data.Store object as
11843 * a cache of Roo.data.Records.
11845 readRecords : function(o){
11847 * After any data loads, the raw JSON data is available for further custom processing.
11851 var s = this.meta, Record = this.recordType,
11852 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11854 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11856 if(s.totalProperty) {
11857 this.getTotal = this.getJsonAccessor(s.totalProperty);
11859 if(s.successProperty) {
11860 this.getSuccess = this.getJsonAccessor(s.successProperty);
11862 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11864 var g = this.getJsonAccessor(s.id);
11865 this.getId = function(rec) {
11867 return (r === undefined || r === "") ? null : r;
11870 this.getId = function(){return null;};
11873 for(var jj = 0; jj < fl; jj++){
11875 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11876 this.ef[jj] = this.getJsonAccessor(map);
11880 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11881 if(s.totalProperty){
11882 var vt = parseInt(this.getTotal(o), 10);
11887 if(s.successProperty){
11888 var vs = this.getSuccess(o);
11889 if(vs === false || vs === 'false'){
11894 for(var i = 0; i < c; i++){
11897 var id = this.getId(n);
11898 for(var j = 0; j < fl; j++){
11900 var v = this.ef[j](n);
11902 Roo.log('missing convert for ' + f.name);
11906 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11908 var record = new Record(values, id);
11910 records[i] = record;
11916 totalRecords : totalRecords
11921 * Ext JS Library 1.1.1
11922 * Copyright(c) 2006-2007, Ext JS, LLC.
11924 * Originally Released Under LGPL - original licence link has changed is not relivant.
11927 * <script type="text/javascript">
11931 * @class Roo.data.ArrayReader
11932 * @extends Roo.data.DataReader
11933 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11934 * Each element of that Array represents a row of data fields. The
11935 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11936 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11940 var RecordDef = Roo.data.Record.create([
11941 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11942 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11944 var myReader = new Roo.data.ArrayReader({
11945 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11949 * This would consume an Array like this:
11951 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11953 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11955 * Create a new JsonReader
11956 * @param {Object} meta Metadata configuration options.
11957 * @param {Object} recordType Either an Array of field definition objects
11958 * as specified to {@link Roo.data.Record#create},
11959 * or an {@link Roo.data.Record} object
11960 * created using {@link Roo.data.Record#create}.
11962 Roo.data.ArrayReader = function(meta, recordType){
11963 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11968 * Create a data block containing Roo.data.Records from an XML document.
11969 * @param {Object} o An Array of row objects which represents the dataset.
11970 * @return {Object} data A data block which is used by an Roo.data.Store object as
11971 * a cache of Roo.data.Records.
11973 readRecords : function(o){
11974 var sid = this.meta ? this.meta.id : null;
11975 var recordType = this.recordType, fields = recordType.prototype.fields;
11978 for(var i = 0; i < root.length; i++){
11981 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11982 for(var j = 0, jlen = fields.length; j < jlen; j++){
11983 var f = fields.items[j];
11984 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11985 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11987 values[f.name] = v;
11989 var record = new recordType(values, id);
11991 records[records.length] = record;
11995 totalRecords : records.length
12004 * @class Roo.bootstrap.ComboBox
12005 * @extends Roo.bootstrap.TriggerField
12006 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12007 * @cfg {Boolean} append (true|false) default false
12008 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12009 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12010 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12011 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12012 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12013 * @cfg {Boolean} animate default true
12014 * @cfg {Boolean} emptyResultText only for touch device
12015 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12017 * Create a new ComboBox.
12018 * @param {Object} config Configuration options
12020 Roo.bootstrap.ComboBox = function(config){
12021 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12025 * Fires when the dropdown list is expanded
12026 * @param {Roo.bootstrap.ComboBox} combo This combo box
12031 * Fires when the dropdown list is collapsed
12032 * @param {Roo.bootstrap.ComboBox} combo This combo box
12036 * @event beforeselect
12037 * Fires before a list item is selected. Return false to cancel the selection.
12038 * @param {Roo.bootstrap.ComboBox} combo This combo box
12039 * @param {Roo.data.Record} record The data record returned from the underlying store
12040 * @param {Number} index The index of the selected item in the dropdown list
12042 'beforeselect' : true,
12045 * Fires when a list item is selected
12046 * @param {Roo.bootstrap.ComboBox} combo This combo box
12047 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12048 * @param {Number} index The index of the selected item in the dropdown list
12052 * @event beforequery
12053 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12054 * The event object passed has these properties:
12055 * @param {Roo.bootstrap.ComboBox} combo This combo box
12056 * @param {String} query The query
12057 * @param {Boolean} forceAll true to force "all" query
12058 * @param {Boolean} cancel true to cancel the query
12059 * @param {Object} e The query event object
12061 'beforequery': true,
12064 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12065 * @param {Roo.bootstrap.ComboBox} combo This combo box
12070 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12071 * @param {Roo.bootstrap.ComboBox} combo This combo box
12072 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12077 * Fires when the remove value from the combobox array
12078 * @param {Roo.bootstrap.ComboBox} combo This combo box
12082 * @event afterremove
12083 * Fires when the remove value from the combobox array
12084 * @param {Roo.bootstrap.ComboBox} combo This combo box
12086 'afterremove' : true,
12088 * @event specialfilter
12089 * Fires when specialfilter
12090 * @param {Roo.bootstrap.ComboBox} combo This combo box
12092 'specialfilter' : true,
12095 * Fires when tick the element
12096 * @param {Roo.bootstrap.ComboBox} combo This combo box
12100 * @event touchviewdisplay
12101 * Fires when touch view require special display (default is using displayField)
12102 * @param {Roo.bootstrap.ComboBox} combo This combo box
12103 * @param {Object} cfg set html .
12105 'touchviewdisplay' : true
12110 this.tickItems = [];
12112 this.selectedIndex = -1;
12113 if(this.mode == 'local'){
12114 if(config.queryDelay === undefined){
12115 this.queryDelay = 10;
12117 if(config.minChars === undefined){
12123 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12126 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12127 * rendering into an Roo.Editor, defaults to false)
12130 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12131 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12134 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12137 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12138 * the dropdown list (defaults to undefined, with no header element)
12142 * @cfg {String/Roo.Template} tpl The template to use to render the output
12146 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12148 listWidth: undefined,
12150 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12151 * mode = 'remote' or 'text' if mode = 'local')
12153 displayField: undefined,
12156 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12157 * mode = 'remote' or 'value' if mode = 'local').
12158 * Note: use of a valueField requires the user make a selection
12159 * in order for a value to be mapped.
12161 valueField: undefined,
12163 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12168 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12169 * field's data value (defaults to the underlying DOM element's name)
12171 hiddenName: undefined,
12173 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12177 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12179 selectedClass: 'active',
12182 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12186 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12187 * anchor positions (defaults to 'tl-bl')
12189 listAlign: 'tl-bl?',
12191 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12195 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12196 * query specified by the allQuery config option (defaults to 'query')
12198 triggerAction: 'query',
12200 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12201 * (defaults to 4, does not apply if editable = false)
12205 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12206 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12210 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12211 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12215 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12216 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12220 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12221 * when editable = true (defaults to false)
12223 selectOnFocus:false,
12225 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12227 queryParam: 'query',
12229 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12230 * when mode = 'remote' (defaults to 'Loading...')
12232 loadingText: 'Loading...',
12234 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12238 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12242 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12243 * traditional select (defaults to true)
12247 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12251 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12255 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12256 * listWidth has a higher value)
12260 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12261 * allow the user to set arbitrary text into the field (defaults to false)
12263 forceSelection:false,
12265 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12266 * if typeAhead = true (defaults to 250)
12268 typeAheadDelay : 250,
12270 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12271 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12273 valueNotFoundText : undefined,
12275 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12277 blockFocus : false,
12280 * @cfg {Boolean} disableClear Disable showing of clear button.
12282 disableClear : false,
12284 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12286 alwaysQuery : false,
12289 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12294 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12296 invalidClass : "has-warning",
12299 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12301 validClass : "has-success",
12304 * @cfg {Boolean} specialFilter (true|false) special filter default false
12306 specialFilter : false,
12309 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12311 mobileTouchView : true,
12314 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12316 useNativeIOS : false,
12318 ios_options : false,
12330 btnPosition : 'right',
12331 triggerList : true,
12332 showToggleBtn : true,
12334 emptyResultText: 'Empty',
12335 triggerText : 'Select',
12337 // element that contains real text value.. (when hidden is used..)
12339 getAutoCreate : function()
12344 * Render classic select for iso
12347 if(Roo.isIOS && this.useNativeIOS){
12348 cfg = this.getAutoCreateNativeIOS();
12356 if(Roo.isTouch && this.mobileTouchView){
12357 cfg = this.getAutoCreateTouchView();
12364 if(!this.tickable){
12365 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12370 * ComboBox with tickable selections
12373 var align = this.labelAlign || this.parentLabelAlign();
12376 cls : 'form-group roo-combobox-tickable' //input-group
12379 var btn_text_select = '';
12380 var btn_text_done = '';
12381 var btn_text_cancel = '';
12383 if (this.btn_text_show) {
12384 btn_text_select = 'Select';
12385 btn_text_done = 'Done';
12386 btn_text_cancel = 'Cancel';
12391 cls : 'tickable-buttons',
12396 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12397 //html : this.triggerText
12398 html: btn_text_select
12404 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12406 html: btn_text_done
12412 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12414 html: btn_text_cancel
12420 buttons.cn.unshift({
12422 cls: 'roo-select2-search-field-input'
12428 Roo.each(buttons.cn, function(c){
12430 c.cls += ' btn-' + _this.size;
12433 if (_this.disabled) {
12444 cls: 'form-hidden-field'
12448 cls: 'roo-select2-choices',
12452 cls: 'roo-select2-search-field',
12464 cls: 'roo-select2-container input-group roo-select2-container-multi',
12469 // cls: 'typeahead typeahead-long dropdown-menu',
12470 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12475 if(this.hasFeedback && !this.allowBlank){
12479 cls: 'glyphicon form-control-feedback'
12482 combobox.cn.push(feedback);
12485 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12487 // Roo.log("left and has label");
12491 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12492 tooltip : 'This field is required'
12497 cls : 'control-label col-sm-' + this.labelWidth,
12498 html : this.fieldLabel
12502 cls : "col-sm-" + (12 - this.labelWidth),
12510 if(this.indicatorpos == 'right'){
12516 cls : 'control-label col-sm-' + this.labelWidth,
12517 html : this.fieldLabel
12522 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12523 tooltip : 'This field is required'
12526 cls : "col-sm-" + (12 - this.labelWidth),
12537 } else if ( this.fieldLabel.length) {
12538 // Roo.log(" label");
12542 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12543 tooltip : 'This field is required'
12547 //cls : 'input-group-addon',
12548 html : this.fieldLabel
12556 if(this.indicatorpos == 'right'){
12561 //cls : 'input-group-addon',
12562 html : this.fieldLabel
12568 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12569 tooltip : 'This field is required'
12580 // Roo.log(" no label && no align");
12587 ['xs','sm','md','lg'].map(function(size){
12588 if (settings[size]) {
12589 cfg.cls += ' col-' + size + '-' + settings[size];
12597 _initEventsCalled : false,
12600 initEvents: function()
12602 if (this._initEventsCalled) { // as we call render... prevent looping...
12605 this._initEventsCalled = true;
12608 throw "can not find store for combo";
12611 this.store = Roo.factory(this.store, Roo.data);
12613 // if we are building from html. then this element is so complex, that we can not really
12614 // use the rendered HTML.
12615 // so we have to trash and replace the previous code.
12616 if (Roo.XComponent.build_from_html) {
12618 // remove this element....
12619 var e = this.el.dom, k=0;
12620 while (e ) { e = e.previousSibling; ++k;}
12625 this.rendered = false;
12627 this.render(this.parent().getChildContainer(true), k);
12633 if(Roo.isIOS && this.useNativeIOS){
12634 this.initIOSView();
12642 if(Roo.isTouch && this.mobileTouchView){
12643 this.initTouchView();
12648 this.initTickableEvents();
12652 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12654 if(this.hiddenName){
12656 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12658 this.hiddenField.dom.value =
12659 this.hiddenValue !== undefined ? this.hiddenValue :
12660 this.value !== undefined ? this.value : '';
12662 // prevent input submission
12663 this.el.dom.removeAttribute('name');
12664 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12669 // this.el.dom.setAttribute('autocomplete', 'off');
12672 var cls = 'x-combo-list';
12674 //this.list = new Roo.Layer({
12675 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12681 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12682 _this.list.setWidth(lw);
12685 this.list.on('mouseover', this.onViewOver, this);
12686 this.list.on('mousemove', this.onViewMove, this);
12688 this.list.on('scroll', this.onViewScroll, this);
12691 this.list.swallowEvent('mousewheel');
12692 this.assetHeight = 0;
12695 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12696 this.assetHeight += this.header.getHeight();
12699 this.innerList = this.list.createChild({cls:cls+'-inner'});
12700 this.innerList.on('mouseover', this.onViewOver, this);
12701 this.innerList.on('mousemove', this.onViewMove, this);
12702 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12704 if(this.allowBlank && !this.pageSize && !this.disableClear){
12705 this.footer = this.list.createChild({cls:cls+'-ft'});
12706 this.pageTb = new Roo.Toolbar(this.footer);
12710 this.footer = this.list.createChild({cls:cls+'-ft'});
12711 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12712 {pageSize: this.pageSize});
12716 if (this.pageTb && this.allowBlank && !this.disableClear) {
12718 this.pageTb.add(new Roo.Toolbar.Fill(), {
12719 cls: 'x-btn-icon x-btn-clear',
12721 handler: function()
12724 _this.clearValue();
12725 _this.onSelect(false, -1);
12730 this.assetHeight += this.footer.getHeight();
12735 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12738 this.view = new Roo.View(this.list, this.tpl, {
12739 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12741 //this.view.wrapEl.setDisplayed(false);
12742 this.view.on('click', this.onViewClick, this);
12746 this.store.on('beforeload', this.onBeforeLoad, this);
12747 this.store.on('load', this.onLoad, this);
12748 this.store.on('loadexception', this.onLoadException, this);
12750 if(this.resizable){
12751 this.resizer = new Roo.Resizable(this.list, {
12752 pinned:true, handles:'se'
12754 this.resizer.on('resize', function(r, w, h){
12755 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12756 this.listWidth = w;
12757 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12758 this.restrictHeight();
12760 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12763 if(!this.editable){
12764 this.editable = true;
12765 this.setEditable(false);
12770 if (typeof(this.events.add.listeners) != 'undefined') {
12772 this.addicon = this.wrap.createChild(
12773 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12775 this.addicon.on('click', function(e) {
12776 this.fireEvent('add', this);
12779 if (typeof(this.events.edit.listeners) != 'undefined') {
12781 this.editicon = this.wrap.createChild(
12782 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12783 if (this.addicon) {
12784 this.editicon.setStyle('margin-left', '40px');
12786 this.editicon.on('click', function(e) {
12788 // we fire even if inothing is selected..
12789 this.fireEvent('edit', this, this.lastData );
12795 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12796 "up" : function(e){
12797 this.inKeyMode = true;
12801 "down" : function(e){
12802 if(!this.isExpanded()){
12803 this.onTriggerClick();
12805 this.inKeyMode = true;
12810 "enter" : function(e){
12811 // this.onViewClick();
12815 if(this.fireEvent("specialkey", this, e)){
12816 this.onViewClick(false);
12822 "esc" : function(e){
12826 "tab" : function(e){
12829 if(this.fireEvent("specialkey", this, e)){
12830 this.onViewClick(false);
12838 doRelay : function(foo, bar, hname){
12839 if(hname == 'down' || this.scope.isExpanded()){
12840 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12849 this.queryDelay = Math.max(this.queryDelay || 10,
12850 this.mode == 'local' ? 10 : 250);
12853 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12855 if(this.typeAhead){
12856 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12858 if(this.editable !== false){
12859 this.inputEl().on("keyup", this.onKeyUp, this);
12861 if(this.forceSelection){
12862 this.inputEl().on('blur', this.doForce, this);
12866 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12867 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12871 initTickableEvents: function()
12875 if(this.hiddenName){
12877 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12879 this.hiddenField.dom.value =
12880 this.hiddenValue !== undefined ? this.hiddenValue :
12881 this.value !== undefined ? this.value : '';
12883 // prevent input submission
12884 this.el.dom.removeAttribute('name');
12885 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12890 // this.list = this.el.select('ul.dropdown-menu',true).first();
12892 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12893 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12894 if(this.triggerList){
12895 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12898 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12899 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12901 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12902 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12904 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12905 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12907 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12908 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12909 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12912 this.cancelBtn.hide();
12917 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12918 _this.list.setWidth(lw);
12921 this.list.on('mouseover', this.onViewOver, this);
12922 this.list.on('mousemove', this.onViewMove, this);
12924 this.list.on('scroll', this.onViewScroll, this);
12927 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></li>';
12930 this.view = new Roo.View(this.list, this.tpl, {
12931 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12934 //this.view.wrapEl.setDisplayed(false);
12935 this.view.on('click', this.onViewClick, this);
12939 this.store.on('beforeload', this.onBeforeLoad, this);
12940 this.store.on('load', this.onLoad, this);
12941 this.store.on('loadexception', this.onLoadException, this);
12944 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12945 "up" : function(e){
12946 this.inKeyMode = true;
12950 "down" : function(e){
12951 this.inKeyMode = true;
12955 "enter" : function(e){
12956 if(this.fireEvent("specialkey", this, e)){
12957 this.onViewClick(false);
12963 "esc" : function(e){
12964 this.onTickableFooterButtonClick(e, false, false);
12967 "tab" : function(e){
12968 this.fireEvent("specialkey", this, e);
12970 this.onTickableFooterButtonClick(e, false, false);
12977 doRelay : function(e, fn, key){
12978 if(this.scope.isExpanded()){
12979 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12988 this.queryDelay = Math.max(this.queryDelay || 10,
12989 this.mode == 'local' ? 10 : 250);
12992 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12994 if(this.typeAhead){
12995 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12998 if(this.editable !== false){
12999 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13004 onDestroy : function(){
13006 this.view.setStore(null);
13007 this.view.el.removeAllListeners();
13008 this.view.el.remove();
13009 this.view.purgeListeners();
13012 this.list.dom.innerHTML = '';
13016 this.store.un('beforeload', this.onBeforeLoad, this);
13017 this.store.un('load', this.onLoad, this);
13018 this.store.un('loadexception', this.onLoadException, this);
13020 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13024 fireKey : function(e){
13025 if(e.isNavKeyPress() && !this.list.isVisible()){
13026 this.fireEvent("specialkey", this, e);
13031 onResize: function(w, h){
13032 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13034 // if(typeof w != 'number'){
13035 // // we do not handle it!?!?
13038 // var tw = this.trigger.getWidth();
13039 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13040 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13042 // this.inputEl().setWidth( this.adjustWidth('input', x));
13044 // //this.trigger.setStyle('left', x+'px');
13046 // if(this.list && this.listWidth === undefined){
13047 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13048 // this.list.setWidth(lw);
13049 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13057 * Allow or prevent the user from directly editing the field text. If false is passed,
13058 * the user will only be able to select from the items defined in the dropdown list. This method
13059 * is the runtime equivalent of setting the 'editable' config option at config time.
13060 * @param {Boolean} value True to allow the user to directly edit the field text
13062 setEditable : function(value){
13063 if(value == this.editable){
13066 this.editable = value;
13068 this.inputEl().dom.setAttribute('readOnly', true);
13069 this.inputEl().on('mousedown', this.onTriggerClick, this);
13070 this.inputEl().addClass('x-combo-noedit');
13072 this.inputEl().dom.setAttribute('readOnly', false);
13073 this.inputEl().un('mousedown', this.onTriggerClick, this);
13074 this.inputEl().removeClass('x-combo-noedit');
13080 onBeforeLoad : function(combo,opts){
13081 if(!this.hasFocus){
13085 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13087 this.restrictHeight();
13088 this.selectedIndex = -1;
13092 onLoad : function(){
13094 this.hasQuery = false;
13096 if(!this.hasFocus){
13100 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13101 this.loading.hide();
13104 if(this.store.getCount() > 0){
13106 this.restrictHeight();
13107 if(this.lastQuery == this.allQuery){
13108 if(this.editable && !this.tickable){
13109 this.inputEl().dom.select();
13113 !this.selectByValue(this.value, true) &&
13116 !this.store.lastOptions ||
13117 typeof(this.store.lastOptions.add) == 'undefined' ||
13118 this.store.lastOptions.add != true
13121 this.select(0, true);
13124 if(this.autoFocus){
13127 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13128 this.taTask.delay(this.typeAheadDelay);
13132 this.onEmptyResults();
13138 onLoadException : function()
13140 this.hasQuery = false;
13142 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13143 this.loading.hide();
13146 if(this.tickable && this.editable){
13151 // only causes errors at present
13152 //Roo.log(this.store.reader.jsonData);
13153 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13155 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13161 onTypeAhead : function(){
13162 if(this.store.getCount() > 0){
13163 var r = this.store.getAt(0);
13164 var newValue = r.data[this.displayField];
13165 var len = newValue.length;
13166 var selStart = this.getRawValue().length;
13168 if(selStart != len){
13169 this.setRawValue(newValue);
13170 this.selectText(selStart, newValue.length);
13176 onSelect : function(record, index){
13178 if(this.fireEvent('beforeselect', this, record, index) !== false){
13180 this.setFromData(index > -1 ? record.data : false);
13183 this.fireEvent('select', this, record, index);
13188 * Returns the currently selected field value or empty string if no value is set.
13189 * @return {String} value The selected value
13191 getValue : function()
13193 if(Roo.isIOS && this.useNativeIOS){
13194 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13198 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13201 if(this.valueField){
13202 return typeof this.value != 'undefined' ? this.value : '';
13204 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13208 getRawValue : function()
13210 if(Roo.isIOS && this.useNativeIOS){
13211 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13214 var v = this.inputEl().getValue();
13220 * Clears any text/value currently set in the field
13222 clearValue : function(){
13224 if(this.hiddenField){
13225 this.hiddenField.dom.value = '';
13228 this.setRawValue('');
13229 this.lastSelectionText = '';
13230 this.lastData = false;
13232 var close = this.closeTriggerEl();
13243 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13244 * will be displayed in the field. If the value does not match the data value of an existing item,
13245 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13246 * Otherwise the field will be blank (although the value will still be set).
13247 * @param {String} value The value to match
13249 setValue : function(v)
13251 if(Roo.isIOS && this.useNativeIOS){
13252 this.setIOSValue(v);
13262 if(this.valueField){
13263 var r = this.findRecord(this.valueField, v);
13265 text = r.data[this.displayField];
13266 }else if(this.valueNotFoundText !== undefined){
13267 text = this.valueNotFoundText;
13270 this.lastSelectionText = text;
13271 if(this.hiddenField){
13272 this.hiddenField.dom.value = v;
13274 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13277 var close = this.closeTriggerEl();
13280 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13286 * @property {Object} the last set data for the element
13291 * Sets the value of the field based on a object which is related to the record format for the store.
13292 * @param {Object} value the value to set as. or false on reset?
13294 setFromData : function(o){
13301 var dv = ''; // display value
13302 var vv = ''; // value value..
13304 if (this.displayField) {
13305 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13307 // this is an error condition!!!
13308 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13311 if(this.valueField){
13312 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13315 var close = this.closeTriggerEl();
13318 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13321 if(this.hiddenField){
13322 this.hiddenField.dom.value = vv;
13324 this.lastSelectionText = dv;
13325 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13329 // no hidden field.. - we store the value in 'value', but still display
13330 // display field!!!!
13331 this.lastSelectionText = dv;
13332 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13339 reset : function(){
13340 // overridden so that last data is reset..
13347 this.setValue(this.originalValue);
13348 //this.clearInvalid();
13349 this.lastData = false;
13351 this.view.clearSelections();
13357 findRecord : function(prop, value){
13359 if(this.store.getCount() > 0){
13360 this.store.each(function(r){
13361 if(r.data[prop] == value){
13371 getName: function()
13373 // returns hidden if it's set..
13374 if (!this.rendered) {return ''};
13375 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13379 onViewMove : function(e, t){
13380 this.inKeyMode = false;
13384 onViewOver : function(e, t){
13385 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13388 var item = this.view.findItemFromChild(t);
13391 var index = this.view.indexOf(item);
13392 this.select(index, false);
13397 onViewClick : function(view, doFocus, el, e)
13399 var index = this.view.getSelectedIndexes()[0];
13401 var r = this.store.getAt(index);
13405 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13412 Roo.each(this.tickItems, function(v,k){
13414 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13416 _this.tickItems.splice(k, 1);
13418 if(typeof(e) == 'undefined' && view == false){
13419 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13431 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13432 this.tickItems.push(r.data);
13435 if(typeof(e) == 'undefined' && view == false){
13436 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13443 this.onSelect(r, index);
13445 if(doFocus !== false && !this.blockFocus){
13446 this.inputEl().focus();
13451 restrictHeight : function(){
13452 //this.innerList.dom.style.height = '';
13453 //var inner = this.innerList.dom;
13454 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13455 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13456 //this.list.beginUpdate();
13457 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13458 this.list.alignTo(this.inputEl(), this.listAlign);
13459 this.list.alignTo(this.inputEl(), this.listAlign);
13460 //this.list.endUpdate();
13464 onEmptyResults : function(){
13466 if(this.tickable && this.editable){
13467 this.restrictHeight();
13475 * Returns true if the dropdown list is expanded, else false.
13477 isExpanded : function(){
13478 return this.list.isVisible();
13482 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13483 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13484 * @param {String} value The data value of the item to select
13485 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13486 * selected item if it is not currently in view (defaults to true)
13487 * @return {Boolean} True if the value matched an item in the list, else false
13489 selectByValue : function(v, scrollIntoView){
13490 if(v !== undefined && v !== null){
13491 var r = this.findRecord(this.valueField || this.displayField, v);
13493 this.select(this.store.indexOf(r), scrollIntoView);
13501 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13502 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13503 * @param {Number} index The zero-based index of the list item to select
13504 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13505 * selected item if it is not currently in view (defaults to true)
13507 select : function(index, scrollIntoView){
13508 this.selectedIndex = index;
13509 this.view.select(index);
13510 if(scrollIntoView !== false){
13511 var el = this.view.getNode(index);
13513 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13516 this.list.scrollChildIntoView(el, false);
13522 selectNext : function(){
13523 var ct = this.store.getCount();
13525 if(this.selectedIndex == -1){
13527 }else if(this.selectedIndex < ct-1){
13528 this.select(this.selectedIndex+1);
13534 selectPrev : function(){
13535 var ct = this.store.getCount();
13537 if(this.selectedIndex == -1){
13539 }else if(this.selectedIndex != 0){
13540 this.select(this.selectedIndex-1);
13546 onKeyUp : function(e){
13547 if(this.editable !== false && !e.isSpecialKey()){
13548 this.lastKey = e.getKey();
13549 this.dqTask.delay(this.queryDelay);
13554 validateBlur : function(){
13555 return !this.list || !this.list.isVisible();
13559 initQuery : function(){
13561 var v = this.getRawValue();
13563 if(this.tickable && this.editable){
13564 v = this.tickableInputEl().getValue();
13571 doForce : function(){
13572 if(this.inputEl().dom.value.length > 0){
13573 this.inputEl().dom.value =
13574 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13580 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13581 * query allowing the query action to be canceled if needed.
13582 * @param {String} query The SQL query to execute
13583 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13584 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13585 * saved in the current store (defaults to false)
13587 doQuery : function(q, forceAll){
13589 if(q === undefined || q === null){
13594 forceAll: forceAll,
13598 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13603 forceAll = qe.forceAll;
13604 if(forceAll === true || (q.length >= this.minChars)){
13606 this.hasQuery = true;
13608 if(this.lastQuery != q || this.alwaysQuery){
13609 this.lastQuery = q;
13610 if(this.mode == 'local'){
13611 this.selectedIndex = -1;
13613 this.store.clearFilter();
13616 if(this.specialFilter){
13617 this.fireEvent('specialfilter', this);
13622 this.store.filter(this.displayField, q);
13625 this.store.fireEvent("datachanged", this.store);
13632 this.store.baseParams[this.queryParam] = q;
13634 var options = {params : this.getParams(q)};
13637 options.add = true;
13638 options.params.start = this.page * this.pageSize;
13641 this.store.load(options);
13644 * this code will make the page width larger, at the beginning, the list not align correctly,
13645 * we should expand the list on onLoad
13646 * so command out it
13651 this.selectedIndex = -1;
13656 this.loadNext = false;
13660 getParams : function(q){
13662 //p[this.queryParam] = q;
13666 p.limit = this.pageSize;
13672 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13674 collapse : function(){
13675 if(!this.isExpanded()){
13681 this.hasFocus = false;
13685 this.cancelBtn.hide();
13686 this.trigger.show();
13689 this.tickableInputEl().dom.value = '';
13690 this.tickableInputEl().blur();
13695 Roo.get(document).un('mousedown', this.collapseIf, this);
13696 Roo.get(document).un('mousewheel', this.collapseIf, this);
13697 if (!this.editable) {
13698 Roo.get(document).un('keydown', this.listKeyPress, this);
13700 this.fireEvent('collapse', this);
13706 collapseIf : function(e){
13707 var in_combo = e.within(this.el);
13708 var in_list = e.within(this.list);
13709 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13711 if (in_combo || in_list || is_list) {
13712 //e.stopPropagation();
13717 this.onTickableFooterButtonClick(e, false, false);
13725 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13727 expand : function(){
13729 if(this.isExpanded() || !this.hasFocus){
13733 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13734 this.list.setWidth(lw);
13740 this.restrictHeight();
13744 this.tickItems = Roo.apply([], this.item);
13747 this.cancelBtn.show();
13748 this.trigger.hide();
13751 this.tickableInputEl().focus();
13756 Roo.get(document).on('mousedown', this.collapseIf, this);
13757 Roo.get(document).on('mousewheel', this.collapseIf, this);
13758 if (!this.editable) {
13759 Roo.get(document).on('keydown', this.listKeyPress, this);
13762 this.fireEvent('expand', this);
13766 // Implements the default empty TriggerField.onTriggerClick function
13767 onTriggerClick : function(e)
13769 Roo.log('trigger click');
13771 if(this.disabled || !this.triggerList){
13776 this.loadNext = false;
13778 if(this.isExpanded()){
13780 if (!this.blockFocus) {
13781 this.inputEl().focus();
13785 this.hasFocus = true;
13786 if(this.triggerAction == 'all') {
13787 this.doQuery(this.allQuery, true);
13789 this.doQuery(this.getRawValue());
13791 if (!this.blockFocus) {
13792 this.inputEl().focus();
13797 onTickableTriggerClick : function(e)
13804 this.loadNext = false;
13805 this.hasFocus = true;
13807 if(this.triggerAction == 'all') {
13808 this.doQuery(this.allQuery, true);
13810 this.doQuery(this.getRawValue());
13814 onSearchFieldClick : function(e)
13816 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13817 this.onTickableFooterButtonClick(e, false, false);
13821 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13826 this.loadNext = false;
13827 this.hasFocus = true;
13829 if(this.triggerAction == 'all') {
13830 this.doQuery(this.allQuery, true);
13832 this.doQuery(this.getRawValue());
13836 listKeyPress : function(e)
13838 //Roo.log('listkeypress');
13839 // scroll to first matching element based on key pres..
13840 if (e.isSpecialKey()) {
13843 var k = String.fromCharCode(e.getKey()).toUpperCase();
13846 var csel = this.view.getSelectedNodes();
13847 var cselitem = false;
13849 var ix = this.view.indexOf(csel[0]);
13850 cselitem = this.store.getAt(ix);
13851 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13857 this.store.each(function(v) {
13859 // start at existing selection.
13860 if (cselitem.id == v.id) {
13866 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13867 match = this.store.indexOf(v);
13873 if (match === false) {
13874 return true; // no more action?
13877 this.view.select(match);
13878 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13879 sn.scrollIntoView(sn.dom.parentNode, false);
13882 onViewScroll : function(e, t){
13884 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){
13888 this.hasQuery = true;
13890 this.loading = this.list.select('.loading', true).first();
13892 if(this.loading === null){
13893 this.list.createChild({
13895 cls: 'loading roo-select2-more-results roo-select2-active',
13896 html: 'Loading more results...'
13899 this.loading = this.list.select('.loading', true).first();
13901 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13903 this.loading.hide();
13906 this.loading.show();
13911 this.loadNext = true;
13913 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13918 addItem : function(o)
13920 var dv = ''; // display value
13922 if (this.displayField) {
13923 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13925 // this is an error condition!!!
13926 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13933 var choice = this.choices.createChild({
13935 cls: 'roo-select2-search-choice',
13944 cls: 'roo-select2-search-choice-close',
13949 }, this.searchField);
13951 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13953 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13961 this.inputEl().dom.value = '';
13966 onRemoveItem : function(e, _self, o)
13968 e.preventDefault();
13970 this.lastItem = Roo.apply([], this.item);
13972 var index = this.item.indexOf(o.data) * 1;
13975 Roo.log('not this item?!');
13979 this.item.splice(index, 1);
13984 this.fireEvent('remove', this, e);
13990 syncValue : function()
13992 if(!this.item.length){
13999 Roo.each(this.item, function(i){
14000 if(_this.valueField){
14001 value.push(i[_this.valueField]);
14008 this.value = value.join(',');
14010 if(this.hiddenField){
14011 this.hiddenField.dom.value = this.value;
14014 this.store.fireEvent("datachanged", this.store);
14019 clearItem : function()
14021 if(!this.multiple){
14027 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14035 if(this.tickable && !Roo.isTouch){
14036 this.view.refresh();
14040 inputEl: function ()
14042 if(Roo.isIOS && this.useNativeIOS){
14043 return this.el.select('select.roo-ios-select', true).first();
14046 if(Roo.isTouch && this.mobileTouchView){
14047 return this.el.select('input.form-control',true).first();
14051 return this.searchField;
14054 return this.el.select('input.form-control',true).first();
14057 onTickableFooterButtonClick : function(e, btn, el)
14059 e.preventDefault();
14061 this.lastItem = Roo.apply([], this.item);
14063 if(btn && btn.name == 'cancel'){
14064 this.tickItems = Roo.apply([], this.item);
14073 Roo.each(this.tickItems, function(o){
14081 validate : function()
14083 var v = this.getRawValue();
14086 v = this.getValue();
14089 if(this.disabled || this.allowBlank || v.length){
14094 this.markInvalid();
14098 tickableInputEl : function()
14100 if(!this.tickable || !this.editable){
14101 return this.inputEl();
14104 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14108 getAutoCreateTouchView : function()
14113 cls: 'form-group' //input-group
14119 type : this.inputType,
14120 cls : 'form-control x-combo-noedit',
14121 autocomplete: 'new-password',
14122 placeholder : this.placeholder || '',
14127 input.name = this.name;
14131 input.cls += ' input-' + this.size;
14134 if (this.disabled) {
14135 input.disabled = true;
14146 inputblock.cls += ' input-group';
14148 inputblock.cn.unshift({
14150 cls : 'input-group-addon',
14155 if(this.removable && !this.multiple){
14156 inputblock.cls += ' roo-removable';
14158 inputblock.cn.push({
14161 cls : 'roo-combo-removable-btn close'
14165 if(this.hasFeedback && !this.allowBlank){
14167 inputblock.cls += ' has-feedback';
14169 inputblock.cn.push({
14171 cls: 'glyphicon form-control-feedback'
14178 inputblock.cls += (this.before) ? '' : ' input-group';
14180 inputblock.cn.push({
14182 cls : 'input-group-addon',
14193 cls: 'form-hidden-field'
14207 cls: 'form-hidden-field'
14211 cls: 'roo-select2-choices',
14215 cls: 'roo-select2-search-field',
14228 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14234 if(!this.multiple && this.showToggleBtn){
14241 if (this.caret != false) {
14244 cls: 'fa fa-' + this.caret
14251 cls : 'input-group-addon btn dropdown-toggle',
14256 cls: 'combobox-clear',
14270 combobox.cls += ' roo-select2-container-multi';
14273 var align = this.labelAlign || this.parentLabelAlign();
14277 if(this.fieldLabel.length && this.labelWidth){
14279 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14280 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14285 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14286 tooltip : 'This field is required'
14290 cls : 'control-label ' + lw,
14291 html : this.fieldLabel
14302 if(this.indicatorpos == 'right'){
14306 cls : 'control-label ' + lw,
14307 html : this.fieldLabel
14312 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14313 tooltip : 'This field is required'
14325 var settings = this;
14327 ['xs','sm','md','lg'].map(function(size){
14328 if (settings[size]) {
14329 cfg.cls += ' col-' + size + '-' + settings[size];
14336 initTouchView : function()
14338 this.renderTouchView();
14340 this.touchViewEl.on('scroll', function(){
14341 this.el.dom.scrollTop = 0;
14344 this.originalValue = this.getValue();
14346 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14348 this.inputEl().on("click", this.showTouchView, this);
14349 if (this.triggerEl) {
14350 this.triggerEl.on("click", this.showTouchView, this);
14354 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14355 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14357 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14359 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14360 this.store.on('load', this.onTouchViewLoad, this);
14361 this.store.on('loadexception', this.onTouchViewLoadException, this);
14363 if(this.hiddenName){
14365 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14367 this.hiddenField.dom.value =
14368 this.hiddenValue !== undefined ? this.hiddenValue :
14369 this.value !== undefined ? this.value : '';
14371 this.el.dom.removeAttribute('name');
14372 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14376 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14377 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14380 if(this.removable && !this.multiple){
14381 var close = this.closeTriggerEl();
14383 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14384 close.on('click', this.removeBtnClick, this, close);
14388 * fix the bug in Safari iOS8
14390 this.inputEl().on("focus", function(e){
14391 document.activeElement.blur();
14399 renderTouchView : function()
14401 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14402 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14404 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14405 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14407 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14408 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14409 this.touchViewBodyEl.setStyle('overflow', 'auto');
14411 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14412 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14414 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14415 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14419 showTouchView : function()
14425 this.touchViewHeaderEl.hide();
14427 if(this.modalTitle.length){
14428 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14429 this.touchViewHeaderEl.show();
14432 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14433 this.touchViewEl.show();
14435 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14436 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14437 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14439 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14441 if(this.modalTitle.length){
14442 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14445 this.touchViewBodyEl.setHeight(bodyHeight);
14449 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14451 this.touchViewEl.addClass('in');
14454 this.doTouchViewQuery();
14458 hideTouchView : function()
14460 this.touchViewEl.removeClass('in');
14464 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14466 this.touchViewEl.setStyle('display', 'none');
14471 setTouchViewValue : function()
14478 Roo.each(this.tickItems, function(o){
14483 this.hideTouchView();
14486 doTouchViewQuery : function()
14495 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14499 if(!this.alwaysQuery || this.mode == 'local'){
14500 this.onTouchViewLoad();
14507 onTouchViewBeforeLoad : function(combo,opts)
14513 onTouchViewLoad : function()
14515 if(this.store.getCount() < 1){
14516 this.onTouchViewEmptyResults();
14520 this.clearTouchView();
14522 var rawValue = this.getRawValue();
14524 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14526 this.tickItems = [];
14528 this.store.data.each(function(d, rowIndex){
14529 var row = this.touchViewListGroup.createChild(template);
14531 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14532 row.addClass(d.data.cls);
14535 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14538 html : d.data[this.displayField]
14541 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14542 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14545 row.removeClass('selected');
14546 if(!this.multiple && this.valueField &&
14547 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14550 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14551 row.addClass('selected');
14554 if(this.multiple && this.valueField &&
14555 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14559 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14560 this.tickItems.push(d.data);
14563 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14567 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14569 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14571 if(this.modalTitle.length){
14572 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14575 var listHeight = this.touchViewListGroup.getHeight();
14579 if(firstChecked && listHeight > bodyHeight){
14580 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14585 onTouchViewLoadException : function()
14587 this.hideTouchView();
14590 onTouchViewEmptyResults : function()
14592 this.clearTouchView();
14594 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14596 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14600 clearTouchView : function()
14602 this.touchViewListGroup.dom.innerHTML = '';
14605 onTouchViewClick : function(e, el, o)
14607 e.preventDefault();
14610 var rowIndex = o.rowIndex;
14612 var r = this.store.getAt(rowIndex);
14614 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14616 if(!this.multiple){
14617 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14618 c.dom.removeAttribute('checked');
14621 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14623 this.setFromData(r.data);
14625 var close = this.closeTriggerEl();
14631 this.hideTouchView();
14633 this.fireEvent('select', this, r, rowIndex);
14638 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14639 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14640 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14644 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14645 this.addItem(r.data);
14646 this.tickItems.push(r.data);
14650 getAutoCreateNativeIOS : function()
14653 cls: 'form-group' //input-group,
14658 cls : 'roo-ios-select'
14662 combobox.name = this.name;
14665 if (this.disabled) {
14666 combobox.disabled = true;
14669 var settings = this;
14671 ['xs','sm','md','lg'].map(function(size){
14672 if (settings[size]) {
14673 cfg.cls += ' col-' + size + '-' + settings[size];
14683 initIOSView : function()
14685 this.store.on('load', this.onIOSViewLoad, this);
14690 onIOSViewLoad : function()
14692 if(this.store.getCount() < 1){
14696 this.clearIOSView();
14698 if(this.allowBlank) {
14700 var default_text = '-- SELECT --';
14702 var opt = this.inputEl().createChild({
14705 html : default_text
14709 o[this.valueField] = 0;
14710 o[this.displayField] = default_text;
14712 this.ios_options.push({
14719 this.store.data.each(function(d, rowIndex){
14723 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14724 html = d.data[this.displayField];
14729 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14730 value = d.data[this.valueField];
14739 if(this.value == d.data[this.valueField]){
14740 option['selected'] = true;
14743 var opt = this.inputEl().createChild(option);
14745 this.ios_options.push({
14752 this.inputEl().on('change', function(){
14753 this.fireEvent('select', this);
14758 clearIOSView: function()
14760 this.inputEl().dom.innerHTML = '';
14762 this.ios_options = [];
14765 setIOSValue: function(v)
14769 if(!this.ios_options){
14773 Roo.each(this.ios_options, function(opts){
14775 opts.el.dom.removeAttribute('selected');
14777 if(opts.data[this.valueField] != v){
14781 opts.el.dom.setAttribute('selected', true);
14787 * @cfg {Boolean} grow
14791 * @cfg {Number} growMin
14795 * @cfg {Number} growMax
14804 Roo.apply(Roo.bootstrap.ComboBox, {
14808 cls: 'modal-header',
14830 cls: 'list-group-item',
14834 cls: 'roo-combobox-list-group-item-value'
14838 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14852 listItemCheckbox : {
14854 cls: 'list-group-item',
14858 cls: 'roo-combobox-list-group-item-value'
14862 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14878 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14883 cls: 'modal-footer',
14891 cls: 'col-xs-6 text-left',
14894 cls: 'btn btn-danger roo-touch-view-cancel',
14900 cls: 'col-xs-6 text-right',
14903 cls: 'btn btn-success roo-touch-view-ok',
14914 Roo.apply(Roo.bootstrap.ComboBox, {
14916 touchViewTemplate : {
14918 cls: 'modal fade roo-combobox-touch-view',
14922 cls: 'modal-dialog',
14923 style : 'position:fixed', // we have to fix position....
14927 cls: 'modal-content',
14929 Roo.bootstrap.ComboBox.header,
14930 Roo.bootstrap.ComboBox.body,
14931 Roo.bootstrap.ComboBox.footer
14940 * Ext JS Library 1.1.1
14941 * Copyright(c) 2006-2007, Ext JS, LLC.
14943 * Originally Released Under LGPL - original licence link has changed is not relivant.
14946 * <script type="text/javascript">
14951 * @extends Roo.util.Observable
14952 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14953 * This class also supports single and multi selection modes. <br>
14954 * Create a data model bound view:
14956 var store = new Roo.data.Store(...);
14958 var view = new Roo.View({
14960 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14962 singleSelect: true,
14963 selectedClass: "ydataview-selected",
14967 // listen for node click?
14968 view.on("click", function(vw, index, node, e){
14969 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14973 dataModel.load("foobar.xml");
14975 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14977 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14978 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14980 * Note: old style constructor is still suported (container, template, config)
14983 * Create a new View
14984 * @param {Object} config The config object
14987 Roo.View = function(config, depreciated_tpl, depreciated_config){
14989 this.parent = false;
14991 if (typeof(depreciated_tpl) == 'undefined') {
14992 // new way.. - universal constructor.
14993 Roo.apply(this, config);
14994 this.el = Roo.get(this.el);
14997 this.el = Roo.get(config);
14998 this.tpl = depreciated_tpl;
14999 Roo.apply(this, depreciated_config);
15001 this.wrapEl = this.el.wrap().wrap();
15002 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15005 if(typeof(this.tpl) == "string"){
15006 this.tpl = new Roo.Template(this.tpl);
15008 // support xtype ctors..
15009 this.tpl = new Roo.factory(this.tpl, Roo);
15013 this.tpl.compile();
15018 * @event beforeclick
15019 * Fires before a click is processed. Returns false to cancel the default action.
15020 * @param {Roo.View} this
15021 * @param {Number} index The index of the target node
15022 * @param {HTMLElement} node The target node
15023 * @param {Roo.EventObject} e The raw event object
15025 "beforeclick" : true,
15028 * Fires when a template node is clicked.
15029 * @param {Roo.View} this
15030 * @param {Number} index The index of the target node
15031 * @param {HTMLElement} node The target node
15032 * @param {Roo.EventObject} e The raw event object
15037 * Fires when a template node is double clicked.
15038 * @param {Roo.View} this
15039 * @param {Number} index The index of the target node
15040 * @param {HTMLElement} node The target node
15041 * @param {Roo.EventObject} e The raw event object
15045 * @event contextmenu
15046 * Fires when a template node is right clicked.
15047 * @param {Roo.View} this
15048 * @param {Number} index The index of the target node
15049 * @param {HTMLElement} node The target node
15050 * @param {Roo.EventObject} e The raw event object
15052 "contextmenu" : true,
15054 * @event selectionchange
15055 * Fires when the selected nodes change.
15056 * @param {Roo.View} this
15057 * @param {Array} selections Array of the selected nodes
15059 "selectionchange" : true,
15062 * @event beforeselect
15063 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15064 * @param {Roo.View} this
15065 * @param {HTMLElement} node The node to be selected
15066 * @param {Array} selections Array of currently selected nodes
15068 "beforeselect" : true,
15070 * @event preparedata
15071 * Fires on every row to render, to allow you to change the data.
15072 * @param {Roo.View} this
15073 * @param {Object} data to be rendered (change this)
15075 "preparedata" : true
15083 "click": this.onClick,
15084 "dblclick": this.onDblClick,
15085 "contextmenu": this.onContextMenu,
15089 this.selections = [];
15091 this.cmp = new Roo.CompositeElementLite([]);
15093 this.store = Roo.factory(this.store, Roo.data);
15094 this.setStore(this.store, true);
15097 if ( this.footer && this.footer.xtype) {
15099 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15101 this.footer.dataSource = this.store;
15102 this.footer.container = fctr;
15103 this.footer = Roo.factory(this.footer, Roo);
15104 fctr.insertFirst(this.el);
15106 // this is a bit insane - as the paging toolbar seems to detach the el..
15107 // dom.parentNode.parentNode.parentNode
15108 // they get detached?
15112 Roo.View.superclass.constructor.call(this);
15117 Roo.extend(Roo.View, Roo.util.Observable, {
15120 * @cfg {Roo.data.Store} store Data store to load data from.
15125 * @cfg {String|Roo.Element} el The container element.
15130 * @cfg {String|Roo.Template} tpl The template used by this View
15134 * @cfg {String} dataName the named area of the template to use as the data area
15135 * Works with domtemplates roo-name="name"
15139 * @cfg {String} selectedClass The css class to add to selected nodes
15141 selectedClass : "x-view-selected",
15143 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15148 * @cfg {String} text to display on mask (default Loading)
15152 * @cfg {Boolean} multiSelect Allow multiple selection
15154 multiSelect : false,
15156 * @cfg {Boolean} singleSelect Allow single selection
15158 singleSelect: false,
15161 * @cfg {Boolean} toggleSelect - selecting
15163 toggleSelect : false,
15166 * @cfg {Boolean} tickable - selecting
15171 * Returns the element this view is bound to.
15172 * @return {Roo.Element}
15174 getEl : function(){
15175 return this.wrapEl;
15181 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15183 refresh : function(){
15184 //Roo.log('refresh');
15187 // if we are using something like 'domtemplate', then
15188 // the what gets used is:
15189 // t.applySubtemplate(NAME, data, wrapping data..)
15190 // the outer template then get' applied with
15191 // the store 'extra data'
15192 // and the body get's added to the
15193 // roo-name="data" node?
15194 // <span class='roo-tpl-{name}'></span> ?????
15198 this.clearSelections();
15199 this.el.update("");
15201 var records = this.store.getRange();
15202 if(records.length < 1) {
15204 // is this valid?? = should it render a template??
15206 this.el.update(this.emptyText);
15210 if (this.dataName) {
15211 this.el.update(t.apply(this.store.meta)); //????
15212 el = this.el.child('.roo-tpl-' + this.dataName);
15215 for(var i = 0, len = records.length; i < len; i++){
15216 var data = this.prepareData(records[i].data, i, records[i]);
15217 this.fireEvent("preparedata", this, data, i, records[i]);
15219 var d = Roo.apply({}, data);
15222 Roo.apply(d, {'roo-id' : Roo.id()});
15226 Roo.each(this.parent.item, function(item){
15227 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15230 Roo.apply(d, {'roo-data-checked' : 'checked'});
15234 html[html.length] = Roo.util.Format.trim(
15236 t.applySubtemplate(this.dataName, d, this.store.meta) :
15243 el.update(html.join(""));
15244 this.nodes = el.dom.childNodes;
15245 this.updateIndexes(0);
15250 * Function to override to reformat the data that is sent to
15251 * the template for each node.
15252 * DEPRICATED - use the preparedata event handler.
15253 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15254 * a JSON object for an UpdateManager bound view).
15256 prepareData : function(data, index, record)
15258 this.fireEvent("preparedata", this, data, index, record);
15262 onUpdate : function(ds, record){
15263 // Roo.log('on update');
15264 this.clearSelections();
15265 var index = this.store.indexOf(record);
15266 var n = this.nodes[index];
15267 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15268 n.parentNode.removeChild(n);
15269 this.updateIndexes(index, index);
15275 onAdd : function(ds, records, index)
15277 //Roo.log(['on Add', ds, records, index] );
15278 this.clearSelections();
15279 if(this.nodes.length == 0){
15283 var n = this.nodes[index];
15284 for(var i = 0, len = records.length; i < len; i++){
15285 var d = this.prepareData(records[i].data, i, records[i]);
15287 this.tpl.insertBefore(n, d);
15290 this.tpl.append(this.el, d);
15293 this.updateIndexes(index);
15296 onRemove : function(ds, record, index){
15297 // Roo.log('onRemove');
15298 this.clearSelections();
15299 var el = this.dataName ?
15300 this.el.child('.roo-tpl-' + this.dataName) :
15303 el.dom.removeChild(this.nodes[index]);
15304 this.updateIndexes(index);
15308 * Refresh an individual node.
15309 * @param {Number} index
15311 refreshNode : function(index){
15312 this.onUpdate(this.store, this.store.getAt(index));
15315 updateIndexes : function(startIndex, endIndex){
15316 var ns = this.nodes;
15317 startIndex = startIndex || 0;
15318 endIndex = endIndex || ns.length - 1;
15319 for(var i = startIndex; i <= endIndex; i++){
15320 ns[i].nodeIndex = i;
15325 * Changes the data store this view uses and refresh the view.
15326 * @param {Store} store
15328 setStore : function(store, initial){
15329 if(!initial && this.store){
15330 this.store.un("datachanged", this.refresh);
15331 this.store.un("add", this.onAdd);
15332 this.store.un("remove", this.onRemove);
15333 this.store.un("update", this.onUpdate);
15334 this.store.un("clear", this.refresh);
15335 this.store.un("beforeload", this.onBeforeLoad);
15336 this.store.un("load", this.onLoad);
15337 this.store.un("loadexception", this.onLoad);
15341 store.on("datachanged", this.refresh, this);
15342 store.on("add", this.onAdd, this);
15343 store.on("remove", this.onRemove, this);
15344 store.on("update", this.onUpdate, this);
15345 store.on("clear", this.refresh, this);
15346 store.on("beforeload", this.onBeforeLoad, this);
15347 store.on("load", this.onLoad, this);
15348 store.on("loadexception", this.onLoad, this);
15356 * onbeforeLoad - masks the loading area.
15359 onBeforeLoad : function(store,opts)
15361 //Roo.log('onBeforeLoad');
15363 this.el.update("");
15365 this.el.mask(this.mask ? this.mask : "Loading" );
15367 onLoad : function ()
15374 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15375 * @param {HTMLElement} node
15376 * @return {HTMLElement} The template node
15378 findItemFromChild : function(node){
15379 var el = this.dataName ?
15380 this.el.child('.roo-tpl-' + this.dataName,true) :
15383 if(!node || node.parentNode == el){
15386 var p = node.parentNode;
15387 while(p && p != el){
15388 if(p.parentNode == el){
15397 onClick : function(e){
15398 var item = this.findItemFromChild(e.getTarget());
15400 var index = this.indexOf(item);
15401 if(this.onItemClick(item, index, e) !== false){
15402 this.fireEvent("click", this, index, item, e);
15405 this.clearSelections();
15410 onContextMenu : function(e){
15411 var item = this.findItemFromChild(e.getTarget());
15413 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15418 onDblClick : function(e){
15419 var item = this.findItemFromChild(e.getTarget());
15421 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15425 onItemClick : function(item, index, e)
15427 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15430 if (this.toggleSelect) {
15431 var m = this.isSelected(item) ? 'unselect' : 'select';
15434 _t[m](item, true, false);
15437 if(this.multiSelect || this.singleSelect){
15438 if(this.multiSelect && e.shiftKey && this.lastSelection){
15439 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15441 this.select(item, this.multiSelect && e.ctrlKey);
15442 this.lastSelection = item;
15445 if(!this.tickable){
15446 e.preventDefault();
15454 * Get the number of selected nodes.
15457 getSelectionCount : function(){
15458 return this.selections.length;
15462 * Get the currently selected nodes.
15463 * @return {Array} An array of HTMLElements
15465 getSelectedNodes : function(){
15466 return this.selections;
15470 * Get the indexes of the selected nodes.
15473 getSelectedIndexes : function(){
15474 var indexes = [], s = this.selections;
15475 for(var i = 0, len = s.length; i < len; i++){
15476 indexes.push(s[i].nodeIndex);
15482 * Clear all selections
15483 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15485 clearSelections : function(suppressEvent){
15486 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15487 this.cmp.elements = this.selections;
15488 this.cmp.removeClass(this.selectedClass);
15489 this.selections = [];
15490 if(!suppressEvent){
15491 this.fireEvent("selectionchange", this, this.selections);
15497 * Returns true if the passed node is selected
15498 * @param {HTMLElement/Number} node The node or node index
15499 * @return {Boolean}
15501 isSelected : function(node){
15502 var s = this.selections;
15506 node = this.getNode(node);
15507 return s.indexOf(node) !== -1;
15512 * @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
15513 * @param {Boolean} keepExisting (optional) true to keep existing selections
15514 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15516 select : function(nodeInfo, keepExisting, suppressEvent){
15517 if(nodeInfo instanceof Array){
15519 this.clearSelections(true);
15521 for(var i = 0, len = nodeInfo.length; i < len; i++){
15522 this.select(nodeInfo[i], true, true);
15526 var node = this.getNode(nodeInfo);
15527 if(!node || this.isSelected(node)){
15528 return; // already selected.
15531 this.clearSelections(true);
15534 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15535 Roo.fly(node).addClass(this.selectedClass);
15536 this.selections.push(node);
15537 if(!suppressEvent){
15538 this.fireEvent("selectionchange", this, this.selections);
15546 * @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
15547 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15548 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15550 unselect : function(nodeInfo, keepExisting, suppressEvent)
15552 if(nodeInfo instanceof Array){
15553 Roo.each(this.selections, function(s) {
15554 this.unselect(s, nodeInfo);
15558 var node = this.getNode(nodeInfo);
15559 if(!node || !this.isSelected(node)){
15560 //Roo.log("not selected");
15561 return; // not selected.
15565 Roo.each(this.selections, function(s) {
15567 Roo.fly(node).removeClass(this.selectedClass);
15574 this.selections= ns;
15575 this.fireEvent("selectionchange", this, this.selections);
15579 * Gets a template node.
15580 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15581 * @return {HTMLElement} The node or null if it wasn't found
15583 getNode : function(nodeInfo){
15584 if(typeof nodeInfo == "string"){
15585 return document.getElementById(nodeInfo);
15586 }else if(typeof nodeInfo == "number"){
15587 return this.nodes[nodeInfo];
15593 * Gets a range template nodes.
15594 * @param {Number} startIndex
15595 * @param {Number} endIndex
15596 * @return {Array} An array of nodes
15598 getNodes : function(start, end){
15599 var ns = this.nodes;
15600 start = start || 0;
15601 end = typeof end == "undefined" ? ns.length - 1 : end;
15604 for(var i = start; i <= end; i++){
15608 for(var i = start; i >= end; i--){
15616 * Finds the index of the passed node
15617 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15618 * @return {Number} The index of the node or -1
15620 indexOf : function(node){
15621 node = this.getNode(node);
15622 if(typeof node.nodeIndex == "number"){
15623 return node.nodeIndex;
15625 var ns = this.nodes;
15626 for(var i = 0, len = ns.length; i < len; i++){
15637 * based on jquery fullcalendar
15641 Roo.bootstrap = Roo.bootstrap || {};
15643 * @class Roo.bootstrap.Calendar
15644 * @extends Roo.bootstrap.Component
15645 * Bootstrap Calendar class
15646 * @cfg {Boolean} loadMask (true|false) default false
15647 * @cfg {Object} header generate the user specific header of the calendar, default false
15650 * Create a new Container
15651 * @param {Object} config The config object
15656 Roo.bootstrap.Calendar = function(config){
15657 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15661 * Fires when a date is selected
15662 * @param {DatePicker} this
15663 * @param {Date} date The selected date
15667 * @event monthchange
15668 * Fires when the displayed month changes
15669 * @param {DatePicker} this
15670 * @param {Date} date The selected month
15672 'monthchange': true,
15674 * @event evententer
15675 * Fires when mouse over an event
15676 * @param {Calendar} this
15677 * @param {event} Event
15679 'evententer': true,
15681 * @event eventleave
15682 * Fires when the mouse leaves an
15683 * @param {Calendar} this
15686 'eventleave': true,
15688 * @event eventclick
15689 * Fires when the mouse click an
15690 * @param {Calendar} this
15699 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15702 * @cfg {Number} startDay
15703 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15711 getAutoCreate : function(){
15714 var fc_button = function(name, corner, style, content ) {
15715 return Roo.apply({},{
15717 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15719 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15722 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15733 style : 'width:100%',
15740 cls : 'fc-header-left',
15742 fc_button('prev', 'left', 'arrow', '‹' ),
15743 fc_button('next', 'right', 'arrow', '›' ),
15744 { tag: 'span', cls: 'fc-header-space' },
15745 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15753 cls : 'fc-header-center',
15757 cls: 'fc-header-title',
15760 html : 'month / year'
15768 cls : 'fc-header-right',
15770 /* fc_button('month', 'left', '', 'month' ),
15771 fc_button('week', '', '', 'week' ),
15772 fc_button('day', 'right', '', 'day' )
15784 header = this.header;
15787 var cal_heads = function() {
15789 // fixme - handle this.
15791 for (var i =0; i < Date.dayNames.length; i++) {
15792 var d = Date.dayNames[i];
15795 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15796 html : d.substring(0,3)
15800 ret[0].cls += ' fc-first';
15801 ret[6].cls += ' fc-last';
15804 var cal_cell = function(n) {
15807 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15812 cls: 'fc-day-number',
15816 cls: 'fc-day-content',
15820 style: 'position: relative;' // height: 17px;
15832 var cal_rows = function() {
15835 for (var r = 0; r < 6; r++) {
15842 for (var i =0; i < Date.dayNames.length; i++) {
15843 var d = Date.dayNames[i];
15844 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15847 row.cn[0].cls+=' fc-first';
15848 row.cn[0].cn[0].style = 'min-height:90px';
15849 row.cn[6].cls+=' fc-last';
15853 ret[0].cls += ' fc-first';
15854 ret[4].cls += ' fc-prev-last';
15855 ret[5].cls += ' fc-last';
15862 cls: 'fc-border-separate',
15863 style : 'width:100%',
15871 cls : 'fc-first fc-last',
15889 cls : 'fc-content',
15890 style : "position: relative;",
15893 cls : 'fc-view fc-view-month fc-grid',
15894 style : 'position: relative',
15895 unselectable : 'on',
15898 cls : 'fc-event-container',
15899 style : 'position:absolute;z-index:8;top:0;left:0;'
15917 initEvents : function()
15920 throw "can not find store for calendar";
15926 style: "text-align:center",
15930 style: "background-color:white;width:50%;margin:250 auto",
15934 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15945 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15947 var size = this.el.select('.fc-content', true).first().getSize();
15948 this.maskEl.setSize(size.width, size.height);
15949 this.maskEl.enableDisplayMode("block");
15950 if(!this.loadMask){
15951 this.maskEl.hide();
15954 this.store = Roo.factory(this.store, Roo.data);
15955 this.store.on('load', this.onLoad, this);
15956 this.store.on('beforeload', this.onBeforeLoad, this);
15960 this.cells = this.el.select('.fc-day',true);
15961 //Roo.log(this.cells);
15962 this.textNodes = this.el.query('.fc-day-number');
15963 this.cells.addClassOnOver('fc-state-hover');
15965 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15966 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15967 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15968 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15970 this.on('monthchange', this.onMonthChange, this);
15972 this.update(new Date().clearTime());
15975 resize : function() {
15976 var sz = this.el.getSize();
15978 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15979 this.el.select('.fc-day-content div',true).setHeight(34);
15984 showPrevMonth : function(e){
15985 this.update(this.activeDate.add("mo", -1));
15987 showToday : function(e){
15988 this.update(new Date().clearTime());
15991 showNextMonth : function(e){
15992 this.update(this.activeDate.add("mo", 1));
15996 showPrevYear : function(){
15997 this.update(this.activeDate.add("y", -1));
16001 showNextYear : function(){
16002 this.update(this.activeDate.add("y", 1));
16007 update : function(date)
16009 var vd = this.activeDate;
16010 this.activeDate = date;
16011 // if(vd && this.el){
16012 // var t = date.getTime();
16013 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16014 // Roo.log('using add remove');
16016 // this.fireEvent('monthchange', this, date);
16018 // this.cells.removeClass("fc-state-highlight");
16019 // this.cells.each(function(c){
16020 // if(c.dateValue == t){
16021 // c.addClass("fc-state-highlight");
16022 // setTimeout(function(){
16023 // try{c.dom.firstChild.focus();}catch(e){}
16033 var days = date.getDaysInMonth();
16035 var firstOfMonth = date.getFirstDateOfMonth();
16036 var startingPos = firstOfMonth.getDay()-this.startDay;
16038 if(startingPos < this.startDay){
16042 var pm = date.add(Date.MONTH, -1);
16043 var prevStart = pm.getDaysInMonth()-startingPos;
16045 this.cells = this.el.select('.fc-day',true);
16046 this.textNodes = this.el.query('.fc-day-number');
16047 this.cells.addClassOnOver('fc-state-hover');
16049 var cells = this.cells.elements;
16050 var textEls = this.textNodes;
16052 Roo.each(cells, function(cell){
16053 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16056 days += startingPos;
16058 // convert everything to numbers so it's fast
16059 var day = 86400000;
16060 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16063 //Roo.log(prevStart);
16065 var today = new Date().clearTime().getTime();
16066 var sel = date.clearTime().getTime();
16067 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16068 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16069 var ddMatch = this.disabledDatesRE;
16070 var ddText = this.disabledDatesText;
16071 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16072 var ddaysText = this.disabledDaysText;
16073 var format = this.format;
16075 var setCellClass = function(cal, cell){
16079 //Roo.log('set Cell Class');
16081 var t = d.getTime();
16085 cell.dateValue = t;
16087 cell.className += " fc-today";
16088 cell.className += " fc-state-highlight";
16089 cell.title = cal.todayText;
16092 // disable highlight in other month..
16093 //cell.className += " fc-state-highlight";
16098 cell.className = " fc-state-disabled";
16099 cell.title = cal.minText;
16103 cell.className = " fc-state-disabled";
16104 cell.title = cal.maxText;
16108 if(ddays.indexOf(d.getDay()) != -1){
16109 cell.title = ddaysText;
16110 cell.className = " fc-state-disabled";
16113 if(ddMatch && format){
16114 var fvalue = d.dateFormat(format);
16115 if(ddMatch.test(fvalue)){
16116 cell.title = ddText.replace("%0", fvalue);
16117 cell.className = " fc-state-disabled";
16121 if (!cell.initialClassName) {
16122 cell.initialClassName = cell.dom.className;
16125 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16130 for(; i < startingPos; i++) {
16131 textEls[i].innerHTML = (++prevStart);
16132 d.setDate(d.getDate()+1);
16134 cells[i].className = "fc-past fc-other-month";
16135 setCellClass(this, cells[i]);
16140 for(; i < days; i++){
16141 intDay = i - startingPos + 1;
16142 textEls[i].innerHTML = (intDay);
16143 d.setDate(d.getDate()+1);
16145 cells[i].className = ''; // "x-date-active";
16146 setCellClass(this, cells[i]);
16150 for(; i < 42; i++) {
16151 textEls[i].innerHTML = (++extraDays);
16152 d.setDate(d.getDate()+1);
16154 cells[i].className = "fc-future fc-other-month";
16155 setCellClass(this, cells[i]);
16158 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16160 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16162 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16163 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16165 if(totalRows != 6){
16166 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16167 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16170 this.fireEvent('monthchange', this, date);
16174 if(!this.internalRender){
16175 var main = this.el.dom.firstChild;
16176 var w = main.offsetWidth;
16177 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16178 Roo.fly(main).setWidth(w);
16179 this.internalRender = true;
16180 // opera does not respect the auto grow header center column
16181 // then, after it gets a width opera refuses to recalculate
16182 // without a second pass
16183 if(Roo.isOpera && !this.secondPass){
16184 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16185 this.secondPass = true;
16186 this.update.defer(10, this, [date]);
16193 findCell : function(dt) {
16194 dt = dt.clearTime().getTime();
16196 this.cells.each(function(c){
16197 //Roo.log("check " +c.dateValue + '?=' + dt);
16198 if(c.dateValue == dt){
16208 findCells : function(ev) {
16209 var s = ev.start.clone().clearTime().getTime();
16211 var e= ev.end.clone().clearTime().getTime();
16214 this.cells.each(function(c){
16215 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16217 if(c.dateValue > e){
16220 if(c.dateValue < s){
16229 // findBestRow: function(cells)
16233 // for (var i =0 ; i < cells.length;i++) {
16234 // ret = Math.max(cells[i].rows || 0,ret);
16241 addItem : function(ev)
16243 // look for vertical location slot in
16244 var cells = this.findCells(ev);
16246 // ev.row = this.findBestRow(cells);
16248 // work out the location.
16252 for(var i =0; i < cells.length; i++) {
16254 cells[i].row = cells[0].row;
16257 cells[i].row = cells[i].row + 1;
16267 if (crow.start.getY() == cells[i].getY()) {
16269 crow.end = cells[i];
16286 cells[0].events.push(ev);
16288 this.calevents.push(ev);
16291 clearEvents: function() {
16293 if(!this.calevents){
16297 Roo.each(this.cells.elements, function(c){
16303 Roo.each(this.calevents, function(e) {
16304 Roo.each(e.els, function(el) {
16305 el.un('mouseenter' ,this.onEventEnter, this);
16306 el.un('mouseleave' ,this.onEventLeave, this);
16311 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16317 renderEvents: function()
16321 this.cells.each(function(c) {
16330 if(c.row != c.events.length){
16331 r = 4 - (4 - (c.row - c.events.length));
16334 c.events = ev.slice(0, r);
16335 c.more = ev.slice(r);
16337 if(c.more.length && c.more.length == 1){
16338 c.events.push(c.more.pop());
16341 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16345 this.cells.each(function(c) {
16347 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16350 for (var e = 0; e < c.events.length; e++){
16351 var ev = c.events[e];
16352 var rows = ev.rows;
16354 for(var i = 0; i < rows.length; i++) {
16356 // how many rows should it span..
16359 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16360 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16362 unselectable : "on",
16365 cls: 'fc-event-inner',
16369 // cls: 'fc-event-time',
16370 // html : cells.length > 1 ? '' : ev.time
16374 cls: 'fc-event-title',
16375 html : String.format('{0}', ev.title)
16382 cls: 'ui-resizable-handle ui-resizable-e',
16383 html : '  '
16390 cfg.cls += ' fc-event-start';
16392 if ((i+1) == rows.length) {
16393 cfg.cls += ' fc-event-end';
16396 var ctr = _this.el.select('.fc-event-container',true).first();
16397 var cg = ctr.createChild(cfg);
16399 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16400 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16402 var r = (c.more.length) ? 1 : 0;
16403 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16404 cg.setWidth(ebox.right - sbox.x -2);
16406 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16407 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16408 cg.on('click', _this.onEventClick, _this, ev);
16419 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16420 style : 'position: absolute',
16421 unselectable : "on",
16424 cls: 'fc-event-inner',
16428 cls: 'fc-event-title',
16436 cls: 'ui-resizable-handle ui-resizable-e',
16437 html : '  '
16443 var ctr = _this.el.select('.fc-event-container',true).first();
16444 var cg = ctr.createChild(cfg);
16446 var sbox = c.select('.fc-day-content',true).first().getBox();
16447 var ebox = c.select('.fc-day-content',true).first().getBox();
16449 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16450 cg.setWidth(ebox.right - sbox.x -2);
16452 cg.on('click', _this.onMoreEventClick, _this, c.more);
16462 onEventEnter: function (e, el,event,d) {
16463 this.fireEvent('evententer', this, el, event);
16466 onEventLeave: function (e, el,event,d) {
16467 this.fireEvent('eventleave', this, el, event);
16470 onEventClick: function (e, el,event,d) {
16471 this.fireEvent('eventclick', this, el, event);
16474 onMonthChange: function () {
16478 onMoreEventClick: function(e, el, more)
16482 this.calpopover.placement = 'right';
16483 this.calpopover.setTitle('More');
16485 this.calpopover.setContent('');
16487 var ctr = this.calpopover.el.select('.popover-content', true).first();
16489 Roo.each(more, function(m){
16491 cls : 'fc-event-hori fc-event-draggable',
16494 var cg = ctr.createChild(cfg);
16496 cg.on('click', _this.onEventClick, _this, m);
16499 this.calpopover.show(el);
16504 onLoad: function ()
16506 this.calevents = [];
16509 if(this.store.getCount() > 0){
16510 this.store.data.each(function(d){
16513 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16514 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16515 time : d.data.start_time,
16516 title : d.data.title,
16517 description : d.data.description,
16518 venue : d.data.venue
16523 this.renderEvents();
16525 if(this.calevents.length && this.loadMask){
16526 this.maskEl.hide();
16530 onBeforeLoad: function()
16532 this.clearEvents();
16534 this.maskEl.show();
16548 * @class Roo.bootstrap.Popover
16549 * @extends Roo.bootstrap.Component
16550 * Bootstrap Popover class
16551 * @cfg {String} html contents of the popover (or false to use children..)
16552 * @cfg {String} title of popover (or false to hide)
16553 * @cfg {String} placement how it is placed
16554 * @cfg {String} trigger click || hover (or false to trigger manually)
16555 * @cfg {String} over what (parent or false to trigger manually.)
16556 * @cfg {Number} delay - delay before showing
16559 * Create a new Popover
16560 * @param {Object} config The config object
16563 Roo.bootstrap.Popover = function(config){
16564 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16570 * After the popover show
16572 * @param {Roo.bootstrap.Popover} this
16577 * After the popover hide
16579 * @param {Roo.bootstrap.Popover} this
16585 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16587 title: 'Fill in a title',
16590 placement : 'right',
16591 trigger : 'hover', // hover
16597 can_build_overlaid : false,
16599 getChildContainer : function()
16601 return this.el.select('.popover-content',true).first();
16604 getAutoCreate : function(){
16607 cls : 'popover roo-dynamic',
16608 style: 'display:block',
16614 cls : 'popover-inner',
16618 cls: 'popover-title',
16622 cls : 'popover-content',
16633 setTitle: function(str)
16636 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16638 setContent: function(str)
16641 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16643 // as it get's added to the bottom of the page.
16644 onRender : function(ct, position)
16646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16648 var cfg = Roo.apply({}, this.getAutoCreate());
16652 cfg.cls += ' ' + this.cls;
16655 cfg.style = this.style;
16657 //Roo.log("adding to ");
16658 this.el = Roo.get(document.body).createChild(cfg, position);
16659 // Roo.log(this.el);
16664 initEvents : function()
16666 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16667 this.el.enableDisplayMode('block');
16669 if (this.over === false) {
16672 if (this.triggers === false) {
16675 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16676 var triggers = this.trigger ? this.trigger.split(' ') : [];
16677 Roo.each(triggers, function(trigger) {
16679 if (trigger == 'click') {
16680 on_el.on('click', this.toggle, this);
16681 } else if (trigger != 'manual') {
16682 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16683 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16685 on_el.on(eventIn ,this.enter, this);
16686 on_el.on(eventOut, this.leave, this);
16697 toggle : function () {
16698 this.hoverState == 'in' ? this.leave() : this.enter();
16701 enter : function () {
16703 clearTimeout(this.timeout);
16705 this.hoverState = 'in';
16707 if (!this.delay || !this.delay.show) {
16712 this.timeout = setTimeout(function () {
16713 if (_t.hoverState == 'in') {
16716 }, this.delay.show)
16719 leave : function() {
16720 clearTimeout(this.timeout);
16722 this.hoverState = 'out';
16724 if (!this.delay || !this.delay.hide) {
16729 this.timeout = setTimeout(function () {
16730 if (_t.hoverState == 'out') {
16733 }, this.delay.hide)
16736 show : function (on_el)
16739 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16743 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16744 if (this.html !== false) {
16745 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16747 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16748 if (!this.title.length) {
16749 this.el.select('.popover-title',true).hide();
16752 var placement = typeof this.placement == 'function' ?
16753 this.placement.call(this, this.el, on_el) :
16756 var autoToken = /\s?auto?\s?/i;
16757 var autoPlace = autoToken.test(placement);
16759 placement = placement.replace(autoToken, '') || 'top';
16763 //this.el.setXY([0,0]);
16765 this.el.dom.style.display='block';
16766 this.el.addClass(placement);
16768 //this.el.appendTo(on_el);
16770 var p = this.getPosition();
16771 var box = this.el.getBox();
16776 var align = Roo.bootstrap.Popover.alignment[placement];
16777 this.el.alignTo(on_el, align[0],align[1]);
16778 //var arrow = this.el.select('.arrow',true).first();
16779 //arrow.set(align[2],
16781 this.el.addClass('in');
16784 if (this.el.hasClass('fade')) {
16788 this.hoverState = 'in';
16790 this.fireEvent('show', this);
16795 this.el.setXY([0,0]);
16796 this.el.removeClass('in');
16798 this.hoverState = null;
16800 this.fireEvent('hide', this);
16805 Roo.bootstrap.Popover.alignment = {
16806 'left' : ['r-l', [-10,0], 'right'],
16807 'right' : ['l-r', [10,0], 'left'],
16808 'bottom' : ['t-b', [0,10], 'top'],
16809 'top' : [ 'b-t', [0,-10], 'bottom']
16820 * @class Roo.bootstrap.Progress
16821 * @extends Roo.bootstrap.Component
16822 * Bootstrap Progress class
16823 * @cfg {Boolean} striped striped of the progress bar
16824 * @cfg {Boolean} active animated of the progress bar
16828 * Create a new Progress
16829 * @param {Object} config The config object
16832 Roo.bootstrap.Progress = function(config){
16833 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16836 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16841 getAutoCreate : function(){
16849 cfg.cls += ' progress-striped';
16853 cfg.cls += ' active';
16872 * @class Roo.bootstrap.ProgressBar
16873 * @extends Roo.bootstrap.Component
16874 * Bootstrap ProgressBar class
16875 * @cfg {Number} aria_valuenow aria-value now
16876 * @cfg {Number} aria_valuemin aria-value min
16877 * @cfg {Number} aria_valuemax aria-value max
16878 * @cfg {String} label label for the progress bar
16879 * @cfg {String} panel (success | info | warning | danger )
16880 * @cfg {String} role role of the progress bar
16881 * @cfg {String} sr_only text
16885 * Create a new ProgressBar
16886 * @param {Object} config The config object
16889 Roo.bootstrap.ProgressBar = function(config){
16890 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16893 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16897 aria_valuemax : 100,
16903 getAutoCreate : function()
16908 cls: 'progress-bar',
16909 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16921 cfg.role = this.role;
16924 if(this.aria_valuenow){
16925 cfg['aria-valuenow'] = this.aria_valuenow;
16928 if(this.aria_valuemin){
16929 cfg['aria-valuemin'] = this.aria_valuemin;
16932 if(this.aria_valuemax){
16933 cfg['aria-valuemax'] = this.aria_valuemax;
16936 if(this.label && !this.sr_only){
16937 cfg.html = this.label;
16941 cfg.cls += ' progress-bar-' + this.panel;
16947 update : function(aria_valuenow)
16949 this.aria_valuenow = aria_valuenow;
16951 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16966 * @class Roo.bootstrap.TabGroup
16967 * @extends Roo.bootstrap.Column
16968 * Bootstrap Column class
16969 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16970 * @cfg {Boolean} carousel true to make the group behave like a carousel
16971 * @cfg {Boolean} bullets show bullets for the panels
16972 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16973 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16974 * @cfg {Boolean} showarrow (true|false) show arrow default true
16977 * Create a new TabGroup
16978 * @param {Object} config The config object
16981 Roo.bootstrap.TabGroup = function(config){
16982 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16984 this.navId = Roo.id();
16987 Roo.bootstrap.TabGroup.register(this);
16991 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16994 transition : false,
16999 slideOnTouch : false,
17002 getAutoCreate : function()
17004 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17006 cfg.cls += ' tab-content';
17008 if (this.carousel) {
17009 cfg.cls += ' carousel slide';
17012 cls : 'carousel-inner',
17016 if(this.bullets && !Roo.isTouch){
17019 cls : 'carousel-bullets',
17023 if(this.bullets_cls){
17024 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17031 cfg.cn[0].cn.push(bullets);
17034 if(this.showarrow){
17035 cfg.cn[0].cn.push({
17037 class : 'carousel-arrow',
17041 class : 'carousel-prev',
17045 class : 'fa fa-chevron-left'
17051 class : 'carousel-next',
17055 class : 'fa fa-chevron-right'
17068 initEvents: function()
17070 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17071 // this.el.on("touchstart", this.onTouchStart, this);
17074 if(this.autoslide){
17077 this.slideFn = window.setInterval(function() {
17078 _this.showPanelNext();
17082 if(this.showarrow){
17083 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17084 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17090 // onTouchStart : function(e, el, o)
17092 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17096 // this.showPanelNext();
17100 getChildContainer : function()
17102 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17106 * register a Navigation item
17107 * @param {Roo.bootstrap.NavItem} the navitem to add
17109 register : function(item)
17111 this.tabs.push( item);
17112 item.navId = this.navId; // not really needed..
17117 getActivePanel : function()
17120 Roo.each(this.tabs, function(t) {
17130 getPanelByName : function(n)
17133 Roo.each(this.tabs, function(t) {
17134 if (t.tabId == n) {
17142 indexOfPanel : function(p)
17145 Roo.each(this.tabs, function(t,i) {
17146 if (t.tabId == p.tabId) {
17155 * show a specific panel
17156 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17157 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17159 showPanel : function (pan)
17161 if(this.transition || typeof(pan) == 'undefined'){
17162 Roo.log("waiting for the transitionend");
17166 if (typeof(pan) == 'number') {
17167 pan = this.tabs[pan];
17170 if (typeof(pan) == 'string') {
17171 pan = this.getPanelByName(pan);
17174 var cur = this.getActivePanel();
17177 Roo.log('pan or acitve pan is undefined');
17181 if (pan.tabId == this.getActivePanel().tabId) {
17185 if (false === cur.fireEvent('beforedeactivate')) {
17189 if(this.bullets > 0 && !Roo.isTouch){
17190 this.setActiveBullet(this.indexOfPanel(pan));
17193 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17195 this.transition = true;
17196 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17197 var lr = dir == 'next' ? 'left' : 'right';
17198 pan.el.addClass(dir); // or prev
17199 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17200 cur.el.addClass(lr); // or right
17201 pan.el.addClass(lr);
17204 cur.el.on('transitionend', function() {
17205 Roo.log("trans end?");
17207 pan.el.removeClass([lr,dir]);
17208 pan.setActive(true);
17210 cur.el.removeClass([lr]);
17211 cur.setActive(false);
17213 _this.transition = false;
17215 }, this, { single: true } );
17220 cur.setActive(false);
17221 pan.setActive(true);
17226 showPanelNext : function()
17228 var i = this.indexOfPanel(this.getActivePanel());
17230 if (i >= this.tabs.length - 1 && !this.autoslide) {
17234 if (i >= this.tabs.length - 1 && this.autoslide) {
17238 this.showPanel(this.tabs[i+1]);
17241 showPanelPrev : function()
17243 var i = this.indexOfPanel(this.getActivePanel());
17245 if (i < 1 && !this.autoslide) {
17249 if (i < 1 && this.autoslide) {
17250 i = this.tabs.length;
17253 this.showPanel(this.tabs[i-1]);
17257 addBullet: function()
17259 if(!this.bullets || Roo.isTouch){
17262 var ctr = this.el.select('.carousel-bullets',true).first();
17263 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17264 var bullet = ctr.createChild({
17265 cls : 'bullet bullet-' + i
17266 },ctr.dom.lastChild);
17271 bullet.on('click', (function(e, el, o, ii, t){
17273 e.preventDefault();
17275 this.showPanel(ii);
17277 if(this.autoslide && this.slideFn){
17278 clearInterval(this.slideFn);
17279 this.slideFn = window.setInterval(function() {
17280 _this.showPanelNext();
17284 }).createDelegate(this, [i, bullet], true));
17289 setActiveBullet : function(i)
17295 Roo.each(this.el.select('.bullet', true).elements, function(el){
17296 el.removeClass('selected');
17299 var bullet = this.el.select('.bullet-' + i, true).first();
17305 bullet.addClass('selected');
17316 Roo.apply(Roo.bootstrap.TabGroup, {
17320 * register a Navigation Group
17321 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17323 register : function(navgrp)
17325 this.groups[navgrp.navId] = navgrp;
17329 * fetch a Navigation Group based on the navigation ID
17330 * if one does not exist , it will get created.
17331 * @param {string} the navgroup to add
17332 * @returns {Roo.bootstrap.NavGroup} the navgroup
17334 get: function(navId) {
17335 if (typeof(this.groups[navId]) == 'undefined') {
17336 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17338 return this.groups[navId] ;
17353 * @class Roo.bootstrap.TabPanel
17354 * @extends Roo.bootstrap.Component
17355 * Bootstrap TabPanel class
17356 * @cfg {Boolean} active panel active
17357 * @cfg {String} html panel content
17358 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17359 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17360 * @cfg {String} href click to link..
17364 * Create a new TabPanel
17365 * @param {Object} config The config object
17368 Roo.bootstrap.TabPanel = function(config){
17369 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17373 * Fires when the active status changes
17374 * @param {Roo.bootstrap.TabPanel} this
17375 * @param {Boolean} state the new state
17380 * @event beforedeactivate
17381 * Fires before a tab is de-activated - can be used to do validation on a form.
17382 * @param {Roo.bootstrap.TabPanel} this
17383 * @return {Boolean} false if there is an error
17386 'beforedeactivate': true
17389 this.tabId = this.tabId || Roo.id();
17393 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17401 getAutoCreate : function(){
17404 // item is needed for carousel - not sure if it has any effect otherwise
17405 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17406 html: this.html || ''
17410 cfg.cls += ' active';
17414 cfg.tabId = this.tabId;
17421 initEvents: function()
17423 var p = this.parent();
17425 this.navId = this.navId || p.navId;
17427 if (typeof(this.navId) != 'undefined') {
17428 // not really needed.. but just in case.. parent should be a NavGroup.
17429 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17433 var i = tg.tabs.length - 1;
17435 if(this.active && tg.bullets > 0 && i < tg.bullets){
17436 tg.setActiveBullet(i);
17440 this.el.on('click', this.onClick, this);
17443 this.el.on("touchstart", this.onTouchStart, this);
17444 this.el.on("touchmove", this.onTouchMove, this);
17445 this.el.on("touchend", this.onTouchEnd, this);
17450 onRender : function(ct, position)
17452 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17455 setActive : function(state)
17457 Roo.log("panel - set active " + this.tabId + "=" + state);
17459 this.active = state;
17461 this.el.removeClass('active');
17463 } else if (!this.el.hasClass('active')) {
17464 this.el.addClass('active');
17467 this.fireEvent('changed', this, state);
17470 onClick : function(e)
17472 e.preventDefault();
17474 if(!this.href.length){
17478 window.location.href = this.href;
17487 onTouchStart : function(e)
17489 this.swiping = false;
17491 this.startX = e.browserEvent.touches[0].clientX;
17492 this.startY = e.browserEvent.touches[0].clientY;
17495 onTouchMove : function(e)
17497 this.swiping = true;
17499 this.endX = e.browserEvent.touches[0].clientX;
17500 this.endY = e.browserEvent.touches[0].clientY;
17503 onTouchEnd : function(e)
17510 var tabGroup = this.parent();
17512 if(this.endX > this.startX){ // swiping right
17513 tabGroup.showPanelPrev();
17517 if(this.startX > this.endX){ // swiping left
17518 tabGroup.showPanelNext();
17537 * @class Roo.bootstrap.DateField
17538 * @extends Roo.bootstrap.Input
17539 * Bootstrap DateField class
17540 * @cfg {Number} weekStart default 0
17541 * @cfg {String} viewMode default empty, (months|years)
17542 * @cfg {String} minViewMode default empty, (months|years)
17543 * @cfg {Number} startDate default -Infinity
17544 * @cfg {Number} endDate default Infinity
17545 * @cfg {Boolean} todayHighlight default false
17546 * @cfg {Boolean} todayBtn default false
17547 * @cfg {Boolean} calendarWeeks default false
17548 * @cfg {Object} daysOfWeekDisabled default empty
17549 * @cfg {Boolean} singleMode default false (true | false)
17551 * @cfg {Boolean} keyboardNavigation default true
17552 * @cfg {String} language default en
17555 * Create a new DateField
17556 * @param {Object} config The config object
17559 Roo.bootstrap.DateField = function(config){
17560 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17564 * Fires when this field show.
17565 * @param {Roo.bootstrap.DateField} this
17566 * @param {Mixed} date The date value
17571 * Fires when this field hide.
17572 * @param {Roo.bootstrap.DateField} this
17573 * @param {Mixed} date The date value
17578 * Fires when select a date.
17579 * @param {Roo.bootstrap.DateField} this
17580 * @param {Mixed} date The date value
17584 * @event beforeselect
17585 * Fires when before select a date.
17586 * @param {Roo.bootstrap.DateField} this
17587 * @param {Mixed} date The date value
17589 beforeselect : true
17593 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17596 * @cfg {String} format
17597 * The default date format string which can be overriden for localization support. The format must be
17598 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17602 * @cfg {String} altFormats
17603 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17604 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17606 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17614 todayHighlight : false,
17620 keyboardNavigation: true,
17622 calendarWeeks: false,
17624 startDate: -Infinity,
17628 daysOfWeekDisabled: [],
17632 singleMode : false,
17634 UTCDate: function()
17636 return new Date(Date.UTC.apply(Date, arguments));
17639 UTCToday: function()
17641 var today = new Date();
17642 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17645 getDate: function() {
17646 var d = this.getUTCDate();
17647 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17650 getUTCDate: function() {
17654 setDate: function(d) {
17655 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17658 setUTCDate: function(d) {
17660 this.setValue(this.formatDate(this.date));
17663 onRender: function(ct, position)
17666 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17668 this.language = this.language || 'en';
17669 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17670 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17672 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17673 this.format = this.format || 'm/d/y';
17674 this.isInline = false;
17675 this.isInput = true;
17676 this.component = this.el.select('.add-on', true).first() || false;
17677 this.component = (this.component && this.component.length === 0) ? false : this.component;
17678 this.hasInput = this.component && this.inputEl().length;
17680 if (typeof(this.minViewMode === 'string')) {
17681 switch (this.minViewMode) {
17683 this.minViewMode = 1;
17686 this.minViewMode = 2;
17689 this.minViewMode = 0;
17694 if (typeof(this.viewMode === 'string')) {
17695 switch (this.viewMode) {
17708 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17710 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17712 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17714 this.picker().on('mousedown', this.onMousedown, this);
17715 this.picker().on('click', this.onClick, this);
17717 this.picker().addClass('datepicker-dropdown');
17719 this.startViewMode = this.viewMode;
17721 if(this.singleMode){
17722 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17723 v.setVisibilityMode(Roo.Element.DISPLAY);
17727 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17728 v.setStyle('width', '189px');
17732 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17733 if(!this.calendarWeeks){
17738 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17739 v.attr('colspan', function(i, val){
17740 return parseInt(val) + 1;
17745 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17747 this.setStartDate(this.startDate);
17748 this.setEndDate(this.endDate);
17750 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17757 if(this.isInline) {
17762 picker : function()
17764 return this.pickerEl;
17765 // return this.el.select('.datepicker', true).first();
17768 fillDow: function()
17770 var dowCnt = this.weekStart;
17779 if(this.calendarWeeks){
17787 while (dowCnt < this.weekStart + 7) {
17791 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17795 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17798 fillMonths: function()
17801 var months = this.picker().select('>.datepicker-months td', true).first();
17803 months.dom.innerHTML = '';
17809 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17812 months.createChild(month);
17819 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;
17821 if (this.date < this.startDate) {
17822 this.viewDate = new Date(this.startDate);
17823 } else if (this.date > this.endDate) {
17824 this.viewDate = new Date(this.endDate);
17826 this.viewDate = new Date(this.date);
17834 var d = new Date(this.viewDate),
17835 year = d.getUTCFullYear(),
17836 month = d.getUTCMonth(),
17837 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17838 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17839 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17840 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17841 currentDate = this.date && this.date.valueOf(),
17842 today = this.UTCToday();
17844 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17846 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17848 // this.picker.select('>tfoot th.today').
17849 // .text(dates[this.language].today)
17850 // .toggle(this.todayBtn !== false);
17852 this.updateNavArrows();
17855 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17857 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17859 prevMonth.setUTCDate(day);
17861 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17863 var nextMonth = new Date(prevMonth);
17865 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17867 nextMonth = nextMonth.valueOf();
17869 var fillMonths = false;
17871 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17873 while(prevMonth.valueOf() < nextMonth) {
17876 if (prevMonth.getUTCDay() === this.weekStart) {
17878 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17886 if(this.calendarWeeks){
17887 // ISO 8601: First week contains first thursday.
17888 // ISO also states week starts on Monday, but we can be more abstract here.
17890 // Start of current week: based on weekstart/current date
17891 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17892 // Thursday of this week
17893 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17894 // First Thursday of year, year from thursday
17895 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17896 // Calendar week: ms between thursdays, div ms per day, div 7 days
17897 calWeek = (th - yth) / 864e5 / 7 + 1;
17899 fillMonths.cn.push({
17907 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17909 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17912 if (this.todayHighlight &&
17913 prevMonth.getUTCFullYear() == today.getFullYear() &&
17914 prevMonth.getUTCMonth() == today.getMonth() &&
17915 prevMonth.getUTCDate() == today.getDate()) {
17916 clsName += ' today';
17919 if (currentDate && prevMonth.valueOf() === currentDate) {
17920 clsName += ' active';
17923 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17924 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17925 clsName += ' disabled';
17928 fillMonths.cn.push({
17930 cls: 'day ' + clsName,
17931 html: prevMonth.getDate()
17934 prevMonth.setDate(prevMonth.getDate()+1);
17937 var currentYear = this.date && this.date.getUTCFullYear();
17938 var currentMonth = this.date && this.date.getUTCMonth();
17940 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17942 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17943 v.removeClass('active');
17945 if(currentYear === year && k === currentMonth){
17946 v.addClass('active');
17949 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17950 v.addClass('disabled');
17956 year = parseInt(year/10, 10) * 10;
17958 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17960 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17963 for (var i = -1; i < 11; i++) {
17964 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17966 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17974 showMode: function(dir)
17977 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17980 Roo.each(this.picker().select('>div',true).elements, function(v){
17981 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17984 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17989 if(this.isInline) {
17993 this.picker().removeClass(['bottom', 'top']);
17995 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17997 * place to the top of element!
18001 this.picker().addClass('top');
18002 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18007 this.picker().addClass('bottom');
18009 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18012 parseDate : function(value)
18014 if(!value || value instanceof Date){
18017 var v = Date.parseDate(value, this.format);
18018 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18019 v = Date.parseDate(value, 'Y-m-d');
18021 if(!v && this.altFormats){
18022 if(!this.altFormatsArray){
18023 this.altFormatsArray = this.altFormats.split("|");
18025 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18026 v = Date.parseDate(value, this.altFormatsArray[i]);
18032 formatDate : function(date, fmt)
18034 return (!date || !(date instanceof Date)) ?
18035 date : date.dateFormat(fmt || this.format);
18038 onFocus : function()
18040 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18044 onBlur : function()
18046 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18048 var d = this.inputEl().getValue();
18057 this.picker().show();
18061 this.fireEvent('show', this, this.date);
18066 if(this.isInline) {
18069 this.picker().hide();
18070 this.viewMode = this.startViewMode;
18073 this.fireEvent('hide', this, this.date);
18077 onMousedown: function(e)
18079 e.stopPropagation();
18080 e.preventDefault();
18085 Roo.bootstrap.DateField.superclass.keyup.call(this);
18089 setValue: function(v)
18091 if(this.fireEvent('beforeselect', this, v) !== false){
18092 var d = new Date(this.parseDate(v) ).clearTime();
18094 if(isNaN(d.getTime())){
18095 this.date = this.viewDate = '';
18096 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18100 v = this.formatDate(d);
18102 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18104 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18108 this.fireEvent('select', this, this.date);
18112 getValue: function()
18114 return this.formatDate(this.date);
18117 fireKey: function(e)
18119 if (!this.picker().isVisible()){
18120 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18126 var dateChanged = false,
18128 newDate, newViewDate;
18133 e.preventDefault();
18137 if (!this.keyboardNavigation) {
18140 dir = e.keyCode == 37 ? -1 : 1;
18143 newDate = this.moveYear(this.date, dir);
18144 newViewDate = this.moveYear(this.viewDate, dir);
18145 } else if (e.shiftKey){
18146 newDate = this.moveMonth(this.date, dir);
18147 newViewDate = this.moveMonth(this.viewDate, dir);
18149 newDate = new Date(this.date);
18150 newDate.setUTCDate(this.date.getUTCDate() + dir);
18151 newViewDate = new Date(this.viewDate);
18152 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18154 if (this.dateWithinRange(newDate)){
18155 this.date = newDate;
18156 this.viewDate = newViewDate;
18157 this.setValue(this.formatDate(this.date));
18159 e.preventDefault();
18160 dateChanged = true;
18165 if (!this.keyboardNavigation) {
18168 dir = e.keyCode == 38 ? -1 : 1;
18170 newDate = this.moveYear(this.date, dir);
18171 newViewDate = this.moveYear(this.viewDate, dir);
18172 } else if (e.shiftKey){
18173 newDate = this.moveMonth(this.date, dir);
18174 newViewDate = this.moveMonth(this.viewDate, dir);
18176 newDate = new Date(this.date);
18177 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18178 newViewDate = new Date(this.viewDate);
18179 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18181 if (this.dateWithinRange(newDate)){
18182 this.date = newDate;
18183 this.viewDate = newViewDate;
18184 this.setValue(this.formatDate(this.date));
18186 e.preventDefault();
18187 dateChanged = true;
18191 this.setValue(this.formatDate(this.date));
18193 e.preventDefault();
18196 this.setValue(this.formatDate(this.date));
18210 onClick: function(e)
18212 e.stopPropagation();
18213 e.preventDefault();
18215 var target = e.getTarget();
18217 if(target.nodeName.toLowerCase() === 'i'){
18218 target = Roo.get(target).dom.parentNode;
18221 var nodeName = target.nodeName;
18222 var className = target.className;
18223 var html = target.innerHTML;
18224 //Roo.log(nodeName);
18226 switch(nodeName.toLowerCase()) {
18228 switch(className) {
18234 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18235 switch(this.viewMode){
18237 this.viewDate = this.moveMonth(this.viewDate, dir);
18241 this.viewDate = this.moveYear(this.viewDate, dir);
18247 var date = new Date();
18248 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18250 this.setValue(this.formatDate(this.date));
18257 if (className.indexOf('disabled') < 0) {
18258 this.viewDate.setUTCDate(1);
18259 if (className.indexOf('month') > -1) {
18260 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18262 var year = parseInt(html, 10) || 0;
18263 this.viewDate.setUTCFullYear(year);
18267 if(this.singleMode){
18268 this.setValue(this.formatDate(this.viewDate));
18279 //Roo.log(className);
18280 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18281 var day = parseInt(html, 10) || 1;
18282 var year = this.viewDate.getUTCFullYear(),
18283 month = this.viewDate.getUTCMonth();
18285 if (className.indexOf('old') > -1) {
18292 } else if (className.indexOf('new') > -1) {
18300 //Roo.log([year,month,day]);
18301 this.date = this.UTCDate(year, month, day,0,0,0,0);
18302 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18304 //Roo.log(this.formatDate(this.date));
18305 this.setValue(this.formatDate(this.date));
18312 setStartDate: function(startDate)
18314 this.startDate = startDate || -Infinity;
18315 if (this.startDate !== -Infinity) {
18316 this.startDate = this.parseDate(this.startDate);
18319 this.updateNavArrows();
18322 setEndDate: function(endDate)
18324 this.endDate = endDate || Infinity;
18325 if (this.endDate !== Infinity) {
18326 this.endDate = this.parseDate(this.endDate);
18329 this.updateNavArrows();
18332 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18334 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18335 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18336 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18338 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18339 return parseInt(d, 10);
18342 this.updateNavArrows();
18345 updateNavArrows: function()
18347 if(this.singleMode){
18351 var d = new Date(this.viewDate),
18352 year = d.getUTCFullYear(),
18353 month = d.getUTCMonth();
18355 Roo.each(this.picker().select('.prev', true).elements, function(v){
18357 switch (this.viewMode) {
18360 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18366 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18373 Roo.each(this.picker().select('.next', true).elements, function(v){
18375 switch (this.viewMode) {
18378 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18384 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18392 moveMonth: function(date, dir)
18397 var new_date = new Date(date.valueOf()),
18398 day = new_date.getUTCDate(),
18399 month = new_date.getUTCMonth(),
18400 mag = Math.abs(dir),
18402 dir = dir > 0 ? 1 : -1;
18405 // If going back one month, make sure month is not current month
18406 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18408 return new_date.getUTCMonth() == month;
18410 // If going forward one month, make sure month is as expected
18411 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18413 return new_date.getUTCMonth() != new_month;
18415 new_month = month + dir;
18416 new_date.setUTCMonth(new_month);
18417 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18418 if (new_month < 0 || new_month > 11) {
18419 new_month = (new_month + 12) % 12;
18422 // For magnitudes >1, move one month at a time...
18423 for (var i=0; i<mag; i++) {
18424 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18425 new_date = this.moveMonth(new_date, dir);
18427 // ...then reset the day, keeping it in the new month
18428 new_month = new_date.getUTCMonth();
18429 new_date.setUTCDate(day);
18431 return new_month != new_date.getUTCMonth();
18434 // Common date-resetting loop -- if date is beyond end of month, make it
18437 new_date.setUTCDate(--day);
18438 new_date.setUTCMonth(new_month);
18443 moveYear: function(date, dir)
18445 return this.moveMonth(date, dir*12);
18448 dateWithinRange: function(date)
18450 return date >= this.startDate && date <= this.endDate;
18456 this.picker().remove();
18459 validateValue : function(value)
18461 if(value.length < 1) {
18462 if(this.allowBlank){
18468 if(value.length < this.minLength){
18471 if(value.length > this.maxLength){
18475 var vt = Roo.form.VTypes;
18476 if(!vt[this.vtype](value, this)){
18480 if(typeof this.validator == "function"){
18481 var msg = this.validator(value);
18487 if(this.regex && !this.regex.test(value)){
18491 if(typeof(this.parseDate(value)) == 'undefined'){
18495 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18499 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18509 Roo.apply(Roo.bootstrap.DateField, {
18520 html: '<i class="fa fa-arrow-left"/>'
18530 html: '<i class="fa fa-arrow-right"/>'
18572 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18573 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18574 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18575 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18576 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18589 navFnc: 'FullYear',
18594 navFnc: 'FullYear',
18599 Roo.apply(Roo.bootstrap.DateField, {
18603 cls: 'datepicker dropdown-menu roo-dynamic',
18607 cls: 'datepicker-days',
18611 cls: 'table-condensed',
18613 Roo.bootstrap.DateField.head,
18617 Roo.bootstrap.DateField.footer
18624 cls: 'datepicker-months',
18628 cls: 'table-condensed',
18630 Roo.bootstrap.DateField.head,
18631 Roo.bootstrap.DateField.content,
18632 Roo.bootstrap.DateField.footer
18639 cls: 'datepicker-years',
18643 cls: 'table-condensed',
18645 Roo.bootstrap.DateField.head,
18646 Roo.bootstrap.DateField.content,
18647 Roo.bootstrap.DateField.footer
18666 * @class Roo.bootstrap.TimeField
18667 * @extends Roo.bootstrap.Input
18668 * Bootstrap DateField class
18672 * Create a new TimeField
18673 * @param {Object} config The config object
18676 Roo.bootstrap.TimeField = function(config){
18677 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18681 * Fires when this field show.
18682 * @param {Roo.bootstrap.DateField} thisthis
18683 * @param {Mixed} date The date value
18688 * Fires when this field hide.
18689 * @param {Roo.bootstrap.DateField} this
18690 * @param {Mixed} date The date value
18695 * Fires when select a date.
18696 * @param {Roo.bootstrap.DateField} this
18697 * @param {Mixed} date The date value
18703 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18706 * @cfg {String} format
18707 * The default time format string which can be overriden for localization support. The format must be
18708 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18712 onRender: function(ct, position)
18715 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18717 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18719 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18721 this.pop = this.picker().select('>.datepicker-time',true).first();
18722 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18724 this.picker().on('mousedown', this.onMousedown, this);
18725 this.picker().on('click', this.onClick, this);
18727 this.picker().addClass('datepicker-dropdown');
18732 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18733 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18734 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18735 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18736 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18737 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18741 fireKey: function(e){
18742 if (!this.picker().isVisible()){
18743 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18749 e.preventDefault();
18757 this.onTogglePeriod();
18760 this.onIncrementMinutes();
18763 this.onDecrementMinutes();
18772 onClick: function(e) {
18773 e.stopPropagation();
18774 e.preventDefault();
18777 picker : function()
18779 return this.el.select('.datepicker', true).first();
18782 fillTime: function()
18784 var time = this.pop.select('tbody', true).first();
18786 time.dom.innerHTML = '';
18801 cls: 'hours-up glyphicon glyphicon-chevron-up'
18821 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18842 cls: 'timepicker-hour',
18857 cls: 'timepicker-minute',
18872 cls: 'btn btn-primary period',
18894 cls: 'hours-down glyphicon glyphicon-chevron-down'
18914 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18932 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18939 var hours = this.time.getHours();
18940 var minutes = this.time.getMinutes();
18953 hours = hours - 12;
18957 hours = '0' + hours;
18961 minutes = '0' + minutes;
18964 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18965 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18966 this.pop.select('button', true).first().dom.innerHTML = period;
18972 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18974 var cls = ['bottom'];
18976 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18983 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18988 this.picker().addClass(cls.join('-'));
18992 Roo.each(cls, function(c){
18994 _this.picker().setTop(_this.inputEl().getHeight());
18998 _this.picker().setTop(0 - _this.picker().getHeight());
19003 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19007 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19014 onFocus : function()
19016 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19020 onBlur : function()
19022 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19028 this.picker().show();
19033 this.fireEvent('show', this, this.date);
19038 this.picker().hide();
19041 this.fireEvent('hide', this, this.date);
19044 setTime : function()
19047 this.setValue(this.time.format(this.format));
19049 this.fireEvent('select', this, this.date);
19054 onMousedown: function(e){
19055 e.stopPropagation();
19056 e.preventDefault();
19059 onIncrementHours: function()
19061 Roo.log('onIncrementHours');
19062 this.time = this.time.add(Date.HOUR, 1);
19067 onDecrementHours: function()
19069 Roo.log('onDecrementHours');
19070 this.time = this.time.add(Date.HOUR, -1);
19074 onIncrementMinutes: function()
19076 Roo.log('onIncrementMinutes');
19077 this.time = this.time.add(Date.MINUTE, 1);
19081 onDecrementMinutes: function()
19083 Roo.log('onDecrementMinutes');
19084 this.time = this.time.add(Date.MINUTE, -1);
19088 onTogglePeriod: function()
19090 Roo.log('onTogglePeriod');
19091 this.time = this.time.add(Date.HOUR, 12);
19098 Roo.apply(Roo.bootstrap.TimeField, {
19128 cls: 'btn btn-info ok',
19140 Roo.apply(Roo.bootstrap.TimeField, {
19144 cls: 'datepicker dropdown-menu',
19148 cls: 'datepicker-time',
19152 cls: 'table-condensed',
19154 Roo.bootstrap.TimeField.content,
19155 Roo.bootstrap.TimeField.footer
19174 * @class Roo.bootstrap.MonthField
19175 * @extends Roo.bootstrap.Input
19176 * Bootstrap MonthField class
19178 * @cfg {String} language default en
19181 * Create a new MonthField
19182 * @param {Object} config The config object
19185 Roo.bootstrap.MonthField = function(config){
19186 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19191 * Fires when this field show.
19192 * @param {Roo.bootstrap.MonthField} this
19193 * @param {Mixed} date The date value
19198 * Fires when this field hide.
19199 * @param {Roo.bootstrap.MonthField} this
19200 * @param {Mixed} date The date value
19205 * Fires when select a date.
19206 * @param {Roo.bootstrap.MonthField} this
19207 * @param {String} oldvalue The old value
19208 * @param {String} newvalue The new value
19214 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19216 onRender: function(ct, position)
19219 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19221 this.language = this.language || 'en';
19222 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19223 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19225 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19226 this.isInline = false;
19227 this.isInput = true;
19228 this.component = this.el.select('.add-on', true).first() || false;
19229 this.component = (this.component && this.component.length === 0) ? false : this.component;
19230 this.hasInput = this.component && this.inputEL().length;
19232 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19234 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19236 this.picker().on('mousedown', this.onMousedown, this);
19237 this.picker().on('click', this.onClick, this);
19239 this.picker().addClass('datepicker-dropdown');
19241 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19242 v.setStyle('width', '189px');
19249 if(this.isInline) {
19255 setValue: function(v, suppressEvent)
19257 var o = this.getValue();
19259 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19263 if(suppressEvent !== true){
19264 this.fireEvent('select', this, o, v);
19269 getValue: function()
19274 onClick: function(e)
19276 e.stopPropagation();
19277 e.preventDefault();
19279 var target = e.getTarget();
19281 if(target.nodeName.toLowerCase() === 'i'){
19282 target = Roo.get(target).dom.parentNode;
19285 var nodeName = target.nodeName;
19286 var className = target.className;
19287 var html = target.innerHTML;
19289 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19293 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19295 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19301 picker : function()
19303 return this.pickerEl;
19306 fillMonths: function()
19309 var months = this.picker().select('>.datepicker-months td', true).first();
19311 months.dom.innerHTML = '';
19317 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19320 months.createChild(month);
19329 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19330 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19333 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19334 e.removeClass('active');
19336 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19337 e.addClass('active');
19344 if(this.isInline) {
19348 this.picker().removeClass(['bottom', 'top']);
19350 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19352 * place to the top of element!
19356 this.picker().addClass('top');
19357 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19362 this.picker().addClass('bottom');
19364 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19367 onFocus : function()
19369 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19373 onBlur : function()
19375 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19377 var d = this.inputEl().getValue();
19386 this.picker().show();
19387 this.picker().select('>.datepicker-months', true).first().show();
19391 this.fireEvent('show', this, this.date);
19396 if(this.isInline) {
19399 this.picker().hide();
19400 this.fireEvent('hide', this, this.date);
19404 onMousedown: function(e)
19406 e.stopPropagation();
19407 e.preventDefault();
19412 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19416 fireKey: function(e)
19418 if (!this.picker().isVisible()){
19419 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19430 e.preventDefault();
19434 dir = e.keyCode == 37 ? -1 : 1;
19436 this.vIndex = this.vIndex + dir;
19438 if(this.vIndex < 0){
19442 if(this.vIndex > 11){
19446 if(isNaN(this.vIndex)){
19450 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19456 dir = e.keyCode == 38 ? -1 : 1;
19458 this.vIndex = this.vIndex + dir * 4;
19460 if(this.vIndex < 0){
19464 if(this.vIndex > 11){
19468 if(isNaN(this.vIndex)){
19472 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19477 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19478 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19482 e.preventDefault();
19485 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19486 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19502 this.picker().remove();
19507 Roo.apply(Roo.bootstrap.MonthField, {
19526 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19527 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19532 Roo.apply(Roo.bootstrap.MonthField, {
19536 cls: 'datepicker dropdown-menu roo-dynamic',
19540 cls: 'datepicker-months',
19544 cls: 'table-condensed',
19546 Roo.bootstrap.DateField.content
19566 * @class Roo.bootstrap.CheckBox
19567 * @extends Roo.bootstrap.Input
19568 * Bootstrap CheckBox class
19570 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19571 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19572 * @cfg {String} boxLabel The text that appears beside the checkbox
19573 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19574 * @cfg {Boolean} checked initnal the element
19575 * @cfg {Boolean} inline inline the element (default false)
19576 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19579 * Create a new CheckBox
19580 * @param {Object} config The config object
19583 Roo.bootstrap.CheckBox = function(config){
19584 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19589 * Fires when the element is checked or unchecked.
19590 * @param {Roo.bootstrap.CheckBox} this This input
19591 * @param {Boolean} checked The new checked value
19598 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19600 inputType: 'checkbox',
19608 getAutoCreate : function()
19610 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19616 cfg.cls = 'form-group ' + this.inputType; //input-group
19619 cfg.cls += ' ' + this.inputType + '-inline';
19625 type : this.inputType,
19626 value : this.inputValue,
19627 cls : 'roo-' + this.inputType, //'form-box',
19628 placeholder : this.placeholder || ''
19632 if(this.inputType != 'radio'){
19636 cls : 'roo-hidden-value',
19637 value : this.checked ? this.valueOff : this.inputValue
19642 if (this.weight) { // Validity check?
19643 cfg.cls += " " + this.inputType + "-" + this.weight;
19646 if (this.disabled) {
19647 input.disabled=true;
19651 input.checked = this.checked;
19658 input.name = this.name;
19660 if(this.inputType != 'radio'){
19661 hidden.name = this.name;
19662 input.name = '_hidden_' + this.name;
19667 input.cls += ' input-' + this.size;
19672 ['xs','sm','md','lg'].map(function(size){
19673 if (settings[size]) {
19674 cfg.cls += ' col-' + size + '-' + settings[size];
19678 var inputblock = input;
19680 if (this.before || this.after) {
19683 cls : 'input-group',
19688 inputblock.cn.push({
19690 cls : 'input-group-addon',
19695 inputblock.cn.push(input);
19697 if(this.inputType != 'radio'){
19698 inputblock.cn.push(hidden);
19702 inputblock.cn.push({
19704 cls : 'input-group-addon',
19711 if (align ==='left' && this.fieldLabel.length) {
19712 // Roo.log("left and has label");
19718 cls : 'control-label col-md-' + this.labelWidth,
19719 html : this.fieldLabel
19723 cls : "col-md-" + (12 - this.labelWidth),
19730 } else if ( this.fieldLabel.length) {
19731 // Roo.log(" label");
19735 tag: this.boxLabel ? 'span' : 'label',
19737 cls: 'control-label box-input-label',
19738 //cls : 'input-group-addon',
19739 html : this.fieldLabel
19749 // Roo.log(" no label && no align");
19750 cfg.cn = [ inputblock ] ;
19756 var boxLabelCfg = {
19758 //'for': id, // box label is handled by onclick - so no for...
19760 html: this.boxLabel
19764 boxLabelCfg.tooltip = this.tooltip;
19767 cfg.cn.push(boxLabelCfg);
19770 if(this.inputType != 'radio'){
19771 cfg.cn.push(hidden);
19779 * return the real input element.
19781 inputEl: function ()
19783 return this.el.select('input.roo-' + this.inputType,true).first();
19785 hiddenEl: function ()
19787 return this.el.select('input.roo-hidden-value',true).first();
19790 labelEl: function()
19792 return this.el.select('label.control-label',true).first();
19794 /* depricated... */
19798 return this.labelEl();
19801 boxLabelEl: function()
19803 return this.el.select('label.box-label',true).first();
19806 initEvents : function()
19808 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19810 this.inputEl().on('click', this.onClick, this);
19812 if (this.boxLabel) {
19813 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19816 this.startValue = this.getValue();
19819 Roo.bootstrap.CheckBox.register(this);
19823 onClick : function()
19825 this.setChecked(!this.checked);
19828 setChecked : function(state,suppressEvent)
19830 this.startValue = this.getValue();
19832 if(this.inputType == 'radio'){
19834 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19835 e.dom.checked = false;
19838 this.inputEl().dom.checked = true;
19840 this.inputEl().dom.value = this.inputValue;
19842 if(suppressEvent !== true){
19843 this.fireEvent('check', this, true);
19851 this.checked = state;
19853 this.inputEl().dom.checked = state;
19856 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19858 if(suppressEvent !== true){
19859 this.fireEvent('check', this, state);
19865 getValue : function()
19867 if(this.inputType == 'radio'){
19868 return this.getGroupValue();
19871 return this.hiddenEl().dom.value;
19875 getGroupValue : function()
19877 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19881 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19884 setValue : function(v,suppressEvent)
19886 if(this.inputType == 'radio'){
19887 this.setGroupValue(v, suppressEvent);
19891 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19896 setGroupValue : function(v, suppressEvent)
19898 this.startValue = this.getValue();
19900 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19901 e.dom.checked = false;
19903 if(e.dom.value == v){
19904 e.dom.checked = true;
19908 if(suppressEvent !== true){
19909 this.fireEvent('check', this, true);
19917 validate : function()
19921 (this.inputType == 'radio' && this.validateRadio()) ||
19922 (this.inputType == 'checkbox' && this.validateCheckbox())
19928 this.markInvalid();
19932 validateRadio : function()
19934 if(this.allowBlank){
19940 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19941 if(!e.dom.checked){
19953 validateCheckbox : function()
19956 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19959 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19967 for(var i in group){
19972 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19979 * Mark this field as valid
19981 markValid : function()
19985 this.fireEvent('valid', this);
19987 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19990 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19997 if(this.inputType == 'radio'){
19998 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19999 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20000 e.findParent('.form-group', false, true).addClass(_this.validClass);
20007 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20008 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20012 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20018 for(var i in group){
20019 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20020 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20025 * Mark this field as invalid
20026 * @param {String} msg The validation message
20028 markInvalid : function(msg)
20030 if(this.allowBlank){
20036 this.fireEvent('invalid', this, msg);
20038 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20041 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20045 label.markInvalid();
20048 if(this.inputType == 'radio'){
20049 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20050 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20051 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20058 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20059 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20063 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20069 for(var i in group){
20070 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20071 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20076 clearInvalid : function()
20078 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20080 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20083 label.iconEl.removeClass(label.validClass);
20084 label.iconEl.removeClass(label.invalidClass);
20088 disable : function()
20090 if(this.inputType != 'radio'){
20091 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20098 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20099 _this.getActionEl().addClass(this.disabledClass);
20100 e.dom.disabled = true;
20104 this.disabled = true;
20105 this.fireEvent("disable", this);
20109 enable : function()
20111 if(this.inputType != 'radio'){
20112 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20119 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20120 _this.getActionEl().removeClass(this.disabledClass);
20121 e.dom.disabled = false;
20125 this.disabled = false;
20126 this.fireEvent("enable", this);
20132 Roo.apply(Roo.bootstrap.CheckBox, {
20137 * register a CheckBox Group
20138 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20140 register : function(checkbox)
20142 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20143 this.groups[checkbox.groupId] = {};
20146 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20150 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20154 * fetch a CheckBox Group based on the group ID
20155 * @param {string} the group ID
20156 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20158 get: function(groupId) {
20159 if (typeof(this.groups[groupId]) == 'undefined') {
20163 return this.groups[groupId] ;
20176 * @class Roo.bootstrap.Radio
20177 * @extends Roo.bootstrap.Component
20178 * Bootstrap Radio class
20179 * @cfg {String} boxLabel - the label associated
20180 * @cfg {String} value - the value of radio
20183 * Create a new Radio
20184 * @param {Object} config The config object
20186 Roo.bootstrap.Radio = function(config){
20187 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20191 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20197 getAutoCreate : function()
20201 cls : 'form-group radio',
20206 html : this.boxLabel
20214 initEvents : function()
20216 this.parent().register(this);
20218 this.el.on('click', this.onClick, this);
20222 onClick : function()
20224 this.setChecked(true);
20227 setChecked : function(state, suppressEvent)
20229 this.parent().setValue(this.value, suppressEvent);
20236 //<script type="text/javascript">
20239 * Based Ext JS Library 1.1.1
20240 * Copyright(c) 2006-2007, Ext JS, LLC.
20246 * @class Roo.HtmlEditorCore
20247 * @extends Roo.Component
20248 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20250 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20253 Roo.HtmlEditorCore = function(config){
20256 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20261 * @event initialize
20262 * Fires when the editor is fully initialized (including the iframe)
20263 * @param {Roo.HtmlEditorCore} this
20268 * Fires when the editor is first receives the focus. Any insertion must wait
20269 * until after this event.
20270 * @param {Roo.HtmlEditorCore} this
20274 * @event beforesync
20275 * Fires before the textarea is updated with content from the editor iframe. Return false
20276 * to cancel the sync.
20277 * @param {Roo.HtmlEditorCore} this
20278 * @param {String} html
20282 * @event beforepush
20283 * Fires before the iframe editor is updated with content from the textarea. Return false
20284 * to cancel the push.
20285 * @param {Roo.HtmlEditorCore} this
20286 * @param {String} html
20291 * Fires when the textarea is updated with content from the editor iframe.
20292 * @param {Roo.HtmlEditorCore} this
20293 * @param {String} html
20298 * Fires when the iframe editor is updated with content from the textarea.
20299 * @param {Roo.HtmlEditorCore} this
20300 * @param {String} html
20305 * @event editorevent
20306 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20307 * @param {Roo.HtmlEditorCore} this
20313 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20315 // defaults : white / black...
20316 this.applyBlacklists();
20323 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20327 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20333 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20338 * @cfg {Number} height (in pixels)
20342 * @cfg {Number} width (in pixels)
20347 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20350 stylesheets: false,
20355 // private properties
20356 validationEvent : false,
20358 initialized : false,
20360 sourceEditMode : false,
20361 onFocus : Roo.emptyFn,
20363 hideMode:'offsets',
20367 // blacklist + whitelisted elements..
20374 * Protected method that will not generally be called directly. It
20375 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20376 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20378 getDocMarkup : function(){
20382 // inherit styels from page...??
20383 if (this.stylesheets === false) {
20385 Roo.get(document.head).select('style').each(function(node) {
20386 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20389 Roo.get(document.head).select('link').each(function(node) {
20390 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20393 } else if (!this.stylesheets.length) {
20395 st = '<style type="text/css">' +
20396 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20402 st += '<style type="text/css">' +
20403 'IMG { cursor: pointer } ' +
20407 return '<html><head>' + st +
20408 //<style type="text/css">' +
20409 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20411 ' </head><body class="roo-htmleditor-body"></body></html>';
20415 onRender : function(ct, position)
20418 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20419 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20422 this.el.dom.style.border = '0 none';
20423 this.el.dom.setAttribute('tabIndex', -1);
20424 this.el.addClass('x-hidden hide');
20428 if(Roo.isIE){ // fix IE 1px bogus margin
20429 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20433 this.frameId = Roo.id();
20437 var iframe = this.owner.wrap.createChild({
20439 cls: 'form-control', // bootstrap..
20441 name: this.frameId,
20442 frameBorder : 'no',
20443 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20448 this.iframe = iframe.dom;
20450 this.assignDocWin();
20452 this.doc.designMode = 'on';
20455 this.doc.write(this.getDocMarkup());
20459 var task = { // must defer to wait for browser to be ready
20461 //console.log("run task?" + this.doc.readyState);
20462 this.assignDocWin();
20463 if(this.doc.body || this.doc.readyState == 'complete'){
20465 this.doc.designMode="on";
20469 Roo.TaskMgr.stop(task);
20470 this.initEditor.defer(10, this);
20477 Roo.TaskMgr.start(task);
20482 onResize : function(w, h)
20484 Roo.log('resize: ' +w + ',' + h );
20485 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20489 if(typeof w == 'number'){
20491 this.iframe.style.width = w + 'px';
20493 if(typeof h == 'number'){
20495 this.iframe.style.height = h + 'px';
20497 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20504 * Toggles the editor between standard and source edit mode.
20505 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20507 toggleSourceEdit : function(sourceEditMode){
20509 this.sourceEditMode = sourceEditMode === true;
20511 if(this.sourceEditMode){
20513 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20516 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20517 //this.iframe.className = '';
20520 //this.setSize(this.owner.wrap.getSize());
20521 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20528 * Protected method that will not generally be called directly. If you need/want
20529 * custom HTML cleanup, this is the method you should override.
20530 * @param {String} html The HTML to be cleaned
20531 * return {String} The cleaned HTML
20533 cleanHtml : function(html){
20534 html = String(html);
20535 if(html.length > 5){
20536 if(Roo.isSafari){ // strip safari nonsense
20537 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20540 if(html == ' '){
20547 * HTML Editor -> Textarea
20548 * Protected method that will not generally be called directly. Syncs the contents
20549 * of the editor iframe with the textarea.
20551 syncValue : function(){
20552 if(this.initialized){
20553 var bd = (this.doc.body || this.doc.documentElement);
20554 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20555 var html = bd.innerHTML;
20557 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20558 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20560 html = '<div style="'+m[0]+'">' + html + '</div>';
20563 html = this.cleanHtml(html);
20564 // fix up the special chars.. normaly like back quotes in word...
20565 // however we do not want to do this with chinese..
20566 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20567 var cc = b.charCodeAt();
20569 (cc >= 0x4E00 && cc < 0xA000 ) ||
20570 (cc >= 0x3400 && cc < 0x4E00 ) ||
20571 (cc >= 0xf900 && cc < 0xfb00 )
20577 if(this.owner.fireEvent('beforesync', this, html) !== false){
20578 this.el.dom.value = html;
20579 this.owner.fireEvent('sync', this, html);
20585 * Protected method that will not generally be called directly. Pushes the value of the textarea
20586 * into the iframe editor.
20588 pushValue : function(){
20589 if(this.initialized){
20590 var v = this.el.dom.value.trim();
20592 // if(v.length < 1){
20596 if(this.owner.fireEvent('beforepush', this, v) !== false){
20597 var d = (this.doc.body || this.doc.documentElement);
20599 this.cleanUpPaste();
20600 this.el.dom.value = d.innerHTML;
20601 this.owner.fireEvent('push', this, v);
20607 deferFocus : function(){
20608 this.focus.defer(10, this);
20612 focus : function(){
20613 if(this.win && !this.sourceEditMode){
20620 assignDocWin: function()
20622 var iframe = this.iframe;
20625 this.doc = iframe.contentWindow.document;
20626 this.win = iframe.contentWindow;
20628 // if (!Roo.get(this.frameId)) {
20631 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20632 // this.win = Roo.get(this.frameId).dom.contentWindow;
20634 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20638 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20639 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20644 initEditor : function(){
20645 //console.log("INIT EDITOR");
20646 this.assignDocWin();
20650 this.doc.designMode="on";
20652 this.doc.write(this.getDocMarkup());
20655 var dbody = (this.doc.body || this.doc.documentElement);
20656 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20657 // this copies styles from the containing element into thsi one..
20658 // not sure why we need all of this..
20659 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20661 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20662 //ss['background-attachment'] = 'fixed'; // w3c
20663 dbody.bgProperties = 'fixed'; // ie
20664 //Roo.DomHelper.applyStyles(dbody, ss);
20665 Roo.EventManager.on(this.doc, {
20666 //'mousedown': this.onEditorEvent,
20667 'mouseup': this.onEditorEvent,
20668 'dblclick': this.onEditorEvent,
20669 'click': this.onEditorEvent,
20670 'keyup': this.onEditorEvent,
20675 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20677 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20678 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20680 this.initialized = true;
20682 this.owner.fireEvent('initialize', this);
20687 onDestroy : function(){
20693 //for (var i =0; i < this.toolbars.length;i++) {
20694 // // fixme - ask toolbars for heights?
20695 // this.toolbars[i].onDestroy();
20698 //this.wrap.dom.innerHTML = '';
20699 //this.wrap.remove();
20704 onFirstFocus : function(){
20706 this.assignDocWin();
20709 this.activated = true;
20712 if(Roo.isGecko){ // prevent silly gecko errors
20714 var s = this.win.getSelection();
20715 if(!s.focusNode || s.focusNode.nodeType != 3){
20716 var r = s.getRangeAt(0);
20717 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20722 this.execCmd('useCSS', true);
20723 this.execCmd('styleWithCSS', false);
20726 this.owner.fireEvent('activate', this);
20730 adjustFont: function(btn){
20731 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20732 //if(Roo.isSafari){ // safari
20735 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20736 if(Roo.isSafari){ // safari
20737 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20738 v = (v < 10) ? 10 : v;
20739 v = (v > 48) ? 48 : v;
20740 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20745 v = Math.max(1, v+adjust);
20747 this.execCmd('FontSize', v );
20750 onEditorEvent : function(e)
20752 this.owner.fireEvent('editorevent', this, e);
20753 // this.updateToolbar();
20754 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20757 insertTag : function(tg)
20759 // could be a bit smarter... -> wrap the current selected tRoo..
20760 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20762 range = this.createRange(this.getSelection());
20763 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20764 wrappingNode.appendChild(range.extractContents());
20765 range.insertNode(wrappingNode);
20772 this.execCmd("formatblock", tg);
20776 insertText : function(txt)
20780 var range = this.createRange();
20781 range.deleteContents();
20782 //alert(Sender.getAttribute('label'));
20784 range.insertNode(this.doc.createTextNode(txt));
20790 * Executes a Midas editor command on the editor document and performs necessary focus and
20791 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20792 * @param {String} cmd The Midas command
20793 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20795 relayCmd : function(cmd, value){
20797 this.execCmd(cmd, value);
20798 this.owner.fireEvent('editorevent', this);
20799 //this.updateToolbar();
20800 this.owner.deferFocus();
20804 * Executes a Midas editor command directly on the editor document.
20805 * For visual commands, you should use {@link #relayCmd} instead.
20806 * <b>This should only be called after the editor is initialized.</b>
20807 * @param {String} cmd The Midas command
20808 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20810 execCmd : function(cmd, value){
20811 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20818 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20820 * @param {String} text | dom node..
20822 insertAtCursor : function(text)
20827 if(!this.activated){
20833 var r = this.doc.selection.createRange();
20844 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20848 // from jquery ui (MIT licenced)
20850 var win = this.win;
20852 if (win.getSelection && win.getSelection().getRangeAt) {
20853 range = win.getSelection().getRangeAt(0);
20854 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20855 range.insertNode(node);
20856 } else if (win.document.selection && win.document.selection.createRange) {
20857 // no firefox support
20858 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20859 win.document.selection.createRange().pasteHTML(txt);
20861 // no firefox support
20862 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20863 this.execCmd('InsertHTML', txt);
20872 mozKeyPress : function(e){
20874 var c = e.getCharCode(), cmd;
20877 c = String.fromCharCode(c).toLowerCase();
20891 this.cleanUpPaste.defer(100, this);
20899 e.preventDefault();
20907 fixKeys : function(){ // load time branching for fastest keydown performance
20909 return function(e){
20910 var k = e.getKey(), r;
20913 r = this.doc.selection.createRange();
20916 r.pasteHTML('    ');
20923 r = this.doc.selection.createRange();
20925 var target = r.parentElement();
20926 if(!target || target.tagName.toLowerCase() != 'li'){
20928 r.pasteHTML('<br />');
20934 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20935 this.cleanUpPaste.defer(100, this);
20941 }else if(Roo.isOpera){
20942 return function(e){
20943 var k = e.getKey();
20947 this.execCmd('InsertHTML','    ');
20950 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20951 this.cleanUpPaste.defer(100, this);
20956 }else if(Roo.isSafari){
20957 return function(e){
20958 var k = e.getKey();
20962 this.execCmd('InsertText','\t');
20966 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20967 this.cleanUpPaste.defer(100, this);
20975 getAllAncestors: function()
20977 var p = this.getSelectedNode();
20980 a.push(p); // push blank onto stack..
20981 p = this.getParentElement();
20985 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20989 a.push(this.doc.body);
20993 lastSelNode : false,
20996 getSelection : function()
20998 this.assignDocWin();
20999 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21002 getSelectedNode: function()
21004 // this may only work on Gecko!!!
21006 // should we cache this!!!!
21011 var range = this.createRange(this.getSelection()).cloneRange();
21014 var parent = range.parentElement();
21016 var testRange = range.duplicate();
21017 testRange.moveToElementText(parent);
21018 if (testRange.inRange(range)) {
21021 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21024 parent = parent.parentElement;
21029 // is ancestor a text element.
21030 var ac = range.commonAncestorContainer;
21031 if (ac.nodeType == 3) {
21032 ac = ac.parentNode;
21035 var ar = ac.childNodes;
21038 var other_nodes = [];
21039 var has_other_nodes = false;
21040 for (var i=0;i<ar.length;i++) {
21041 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21044 // fullly contained node.
21046 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21051 // probably selected..
21052 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21053 other_nodes.push(ar[i]);
21057 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21062 has_other_nodes = true;
21064 if (!nodes.length && other_nodes.length) {
21065 nodes= other_nodes;
21067 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21073 createRange: function(sel)
21075 // this has strange effects when using with
21076 // top toolbar - not sure if it's a great idea.
21077 //this.editor.contentWindow.focus();
21078 if (typeof sel != "undefined") {
21080 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21082 return this.doc.createRange();
21085 return this.doc.createRange();
21088 getParentElement: function()
21091 this.assignDocWin();
21092 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21094 var range = this.createRange(sel);
21097 var p = range.commonAncestorContainer;
21098 while (p.nodeType == 3) { // text node
21109 * Range intersection.. the hard stuff...
21113 * [ -- selected range --- ]
21117 * if end is before start or hits it. fail.
21118 * if start is after end or hits it fail.
21120 * if either hits (but other is outside. - then it's not
21126 // @see http://www.thismuchiknow.co.uk/?p=64.
21127 rangeIntersectsNode : function(range, node)
21129 var nodeRange = node.ownerDocument.createRange();
21131 nodeRange.selectNode(node);
21133 nodeRange.selectNodeContents(node);
21136 var rangeStartRange = range.cloneRange();
21137 rangeStartRange.collapse(true);
21139 var rangeEndRange = range.cloneRange();
21140 rangeEndRange.collapse(false);
21142 var nodeStartRange = nodeRange.cloneRange();
21143 nodeStartRange.collapse(true);
21145 var nodeEndRange = nodeRange.cloneRange();
21146 nodeEndRange.collapse(false);
21148 return rangeStartRange.compareBoundaryPoints(
21149 Range.START_TO_START, nodeEndRange) == -1 &&
21150 rangeEndRange.compareBoundaryPoints(
21151 Range.START_TO_START, nodeStartRange) == 1;
21155 rangeCompareNode : function(range, node)
21157 var nodeRange = node.ownerDocument.createRange();
21159 nodeRange.selectNode(node);
21161 nodeRange.selectNodeContents(node);
21165 range.collapse(true);
21167 nodeRange.collapse(true);
21169 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21170 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21172 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21174 var nodeIsBefore = ss == 1;
21175 var nodeIsAfter = ee == -1;
21177 if (nodeIsBefore && nodeIsAfter) {
21180 if (!nodeIsBefore && nodeIsAfter) {
21181 return 1; //right trailed.
21184 if (nodeIsBefore && !nodeIsAfter) {
21185 return 2; // left trailed.
21191 // private? - in a new class?
21192 cleanUpPaste : function()
21194 // cleans up the whole document..
21195 Roo.log('cleanuppaste');
21197 this.cleanUpChildren(this.doc.body);
21198 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21199 if (clean != this.doc.body.innerHTML) {
21200 this.doc.body.innerHTML = clean;
21205 cleanWordChars : function(input) {// change the chars to hex code
21206 var he = Roo.HtmlEditorCore;
21208 var output = input;
21209 Roo.each(he.swapCodes, function(sw) {
21210 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21212 output = output.replace(swapper, sw[1]);
21219 cleanUpChildren : function (n)
21221 if (!n.childNodes.length) {
21224 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21225 this.cleanUpChild(n.childNodes[i]);
21232 cleanUpChild : function (node)
21235 //console.log(node);
21236 if (node.nodeName == "#text") {
21237 // clean up silly Windows -- stuff?
21240 if (node.nodeName == "#comment") {
21241 node.parentNode.removeChild(node);
21242 // clean up silly Windows -- stuff?
21245 var lcname = node.tagName.toLowerCase();
21246 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21247 // whitelist of tags..
21249 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21251 node.parentNode.removeChild(node);
21256 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21258 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21259 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21261 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21262 // remove_keep_children = true;
21265 if (remove_keep_children) {
21266 this.cleanUpChildren(node);
21267 // inserts everything just before this node...
21268 while (node.childNodes.length) {
21269 var cn = node.childNodes[0];
21270 node.removeChild(cn);
21271 node.parentNode.insertBefore(cn, node);
21273 node.parentNode.removeChild(node);
21277 if (!node.attributes || !node.attributes.length) {
21278 this.cleanUpChildren(node);
21282 function cleanAttr(n,v)
21285 if (v.match(/^\./) || v.match(/^\//)) {
21288 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21291 if (v.match(/^#/)) {
21294 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21295 node.removeAttribute(n);
21299 var cwhite = this.cwhite;
21300 var cblack = this.cblack;
21302 function cleanStyle(n,v)
21304 if (v.match(/expression/)) { //XSS?? should we even bother..
21305 node.removeAttribute(n);
21309 var parts = v.split(/;/);
21312 Roo.each(parts, function(p) {
21313 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21317 var l = p.split(':').shift().replace(/\s+/g,'');
21318 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21320 if ( cwhite.length && cblack.indexOf(l) > -1) {
21321 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21322 //node.removeAttribute(n);
21326 // only allow 'c whitelisted system attributes'
21327 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21328 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21329 //node.removeAttribute(n);
21339 if (clean.length) {
21340 node.setAttribute(n, clean.join(';'));
21342 node.removeAttribute(n);
21348 for (var i = node.attributes.length-1; i > -1 ; i--) {
21349 var a = node.attributes[i];
21352 if (a.name.toLowerCase().substr(0,2)=='on') {
21353 node.removeAttribute(a.name);
21356 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21357 node.removeAttribute(a.name);
21360 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21361 cleanAttr(a.name,a.value); // fixme..
21364 if (a.name == 'style') {
21365 cleanStyle(a.name,a.value);
21368 /// clean up MS crap..
21369 // tecnically this should be a list of valid class'es..
21372 if (a.name == 'class') {
21373 if (a.value.match(/^Mso/)) {
21374 node.className = '';
21377 if (a.value.match(/body/)) {
21378 node.className = '';
21389 this.cleanUpChildren(node);
21395 * Clean up MS wordisms...
21397 cleanWord : function(node)
21402 this.cleanWord(this.doc.body);
21405 if (node.nodeName == "#text") {
21406 // clean up silly Windows -- stuff?
21409 if (node.nodeName == "#comment") {
21410 node.parentNode.removeChild(node);
21411 // clean up silly Windows -- stuff?
21415 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21416 node.parentNode.removeChild(node);
21420 // remove - but keep children..
21421 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21422 while (node.childNodes.length) {
21423 var cn = node.childNodes[0];
21424 node.removeChild(cn);
21425 node.parentNode.insertBefore(cn, node);
21427 node.parentNode.removeChild(node);
21428 this.iterateChildren(node, this.cleanWord);
21432 if (node.className.length) {
21434 var cn = node.className.split(/\W+/);
21436 Roo.each(cn, function(cls) {
21437 if (cls.match(/Mso[a-zA-Z]+/)) {
21442 node.className = cna.length ? cna.join(' ') : '';
21444 node.removeAttribute("class");
21448 if (node.hasAttribute("lang")) {
21449 node.removeAttribute("lang");
21452 if (node.hasAttribute("style")) {
21454 var styles = node.getAttribute("style").split(";");
21456 Roo.each(styles, function(s) {
21457 if (!s.match(/:/)) {
21460 var kv = s.split(":");
21461 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21464 // what ever is left... we allow.
21467 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21468 if (!nstyle.length) {
21469 node.removeAttribute('style');
21472 this.iterateChildren(node, this.cleanWord);
21478 * iterateChildren of a Node, calling fn each time, using this as the scole..
21479 * @param {DomNode} node node to iterate children of.
21480 * @param {Function} fn method of this class to call on each item.
21482 iterateChildren : function(node, fn)
21484 if (!node.childNodes.length) {
21487 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21488 fn.call(this, node.childNodes[i])
21494 * cleanTableWidths.
21496 * Quite often pasting from word etc.. results in tables with column and widths.
21497 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21500 cleanTableWidths : function(node)
21505 this.cleanTableWidths(this.doc.body);
21510 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21513 Roo.log(node.tagName);
21514 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21515 this.iterateChildren(node, this.cleanTableWidths);
21518 if (node.hasAttribute('width')) {
21519 node.removeAttribute('width');
21523 if (node.hasAttribute("style")) {
21526 var styles = node.getAttribute("style").split(";");
21528 Roo.each(styles, function(s) {
21529 if (!s.match(/:/)) {
21532 var kv = s.split(":");
21533 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21536 // what ever is left... we allow.
21539 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21540 if (!nstyle.length) {
21541 node.removeAttribute('style');
21545 this.iterateChildren(node, this.cleanTableWidths);
21553 domToHTML : function(currentElement, depth, nopadtext) {
21555 depth = depth || 0;
21556 nopadtext = nopadtext || false;
21558 if (!currentElement) {
21559 return this.domToHTML(this.doc.body);
21562 //Roo.log(currentElement);
21564 var allText = false;
21565 var nodeName = currentElement.nodeName;
21566 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21568 if (nodeName == '#text') {
21570 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21575 if (nodeName != 'BODY') {
21578 // Prints the node tagName, such as <A>, <IMG>, etc
21581 for(i = 0; i < currentElement.attributes.length;i++) {
21583 var aname = currentElement.attributes.item(i).name;
21584 if (!currentElement.attributes.item(i).value.length) {
21587 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21590 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21599 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21602 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21607 // Traverse the tree
21609 var currentElementChild = currentElement.childNodes.item(i);
21610 var allText = true;
21611 var innerHTML = '';
21613 while (currentElementChild) {
21614 // Formatting code (indent the tree so it looks nice on the screen)
21615 var nopad = nopadtext;
21616 if (lastnode == 'SPAN') {
21620 if (currentElementChild.nodeName == '#text') {
21621 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21622 toadd = nopadtext ? toadd : toadd.trim();
21623 if (!nopad && toadd.length > 80) {
21624 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21626 innerHTML += toadd;
21629 currentElementChild = currentElement.childNodes.item(i);
21635 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21637 // Recursively traverse the tree structure of the child node
21638 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21639 lastnode = currentElementChild.nodeName;
21641 currentElementChild=currentElement.childNodes.item(i);
21647 // The remaining code is mostly for formatting the tree
21648 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21653 ret+= "</"+tagName+">";
21659 applyBlacklists : function()
21661 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21662 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21666 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21667 if (b.indexOf(tag) > -1) {
21670 this.white.push(tag);
21674 Roo.each(w, function(tag) {
21675 if (b.indexOf(tag) > -1) {
21678 if (this.white.indexOf(tag) > -1) {
21681 this.white.push(tag);
21686 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21687 if (w.indexOf(tag) > -1) {
21690 this.black.push(tag);
21694 Roo.each(b, function(tag) {
21695 if (w.indexOf(tag) > -1) {
21698 if (this.black.indexOf(tag) > -1) {
21701 this.black.push(tag);
21706 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21707 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21711 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21712 if (b.indexOf(tag) > -1) {
21715 this.cwhite.push(tag);
21719 Roo.each(w, function(tag) {
21720 if (b.indexOf(tag) > -1) {
21723 if (this.cwhite.indexOf(tag) > -1) {
21726 this.cwhite.push(tag);
21731 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21732 if (w.indexOf(tag) > -1) {
21735 this.cblack.push(tag);
21739 Roo.each(b, function(tag) {
21740 if (w.indexOf(tag) > -1) {
21743 if (this.cblack.indexOf(tag) > -1) {
21746 this.cblack.push(tag);
21751 setStylesheets : function(stylesheets)
21753 if(typeof(stylesheets) == 'string'){
21754 Roo.get(this.iframe.contentDocument.head).createChild({
21756 rel : 'stylesheet',
21765 Roo.each(stylesheets, function(s) {
21770 Roo.get(_this.iframe.contentDocument.head).createChild({
21772 rel : 'stylesheet',
21781 removeStylesheets : function()
21785 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21790 // hide stuff that is not compatible
21804 * @event specialkey
21808 * @cfg {String} fieldClass @hide
21811 * @cfg {String} focusClass @hide
21814 * @cfg {String} autoCreate @hide
21817 * @cfg {String} inputType @hide
21820 * @cfg {String} invalidClass @hide
21823 * @cfg {String} invalidText @hide
21826 * @cfg {String} msgFx @hide
21829 * @cfg {String} validateOnBlur @hide
21833 Roo.HtmlEditorCore.white = [
21834 'area', 'br', 'img', 'input', 'hr', 'wbr',
21836 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21837 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21838 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21839 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21840 'table', 'ul', 'xmp',
21842 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21845 'dir', 'menu', 'ol', 'ul', 'dl',
21851 Roo.HtmlEditorCore.black = [
21852 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21854 'base', 'basefont', 'bgsound', 'blink', 'body',
21855 'frame', 'frameset', 'head', 'html', 'ilayer',
21856 'iframe', 'layer', 'link', 'meta', 'object',
21857 'script', 'style' ,'title', 'xml' // clean later..
21859 Roo.HtmlEditorCore.clean = [
21860 'script', 'style', 'title', 'xml'
21862 Roo.HtmlEditorCore.remove = [
21867 Roo.HtmlEditorCore.ablack = [
21871 Roo.HtmlEditorCore.aclean = [
21872 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21876 Roo.HtmlEditorCore.pwhite= [
21877 'http', 'https', 'mailto'
21880 // white listed style attributes.
21881 Roo.HtmlEditorCore.cwhite= [
21882 // 'text-align', /// default is to allow most things..
21888 // black listed style attributes.
21889 Roo.HtmlEditorCore.cblack= [
21890 // 'font-size' -- this can be set by the project
21894 Roo.HtmlEditorCore.swapCodes =[
21913 * @class Roo.bootstrap.HtmlEditor
21914 * @extends Roo.bootstrap.TextArea
21915 * Bootstrap HtmlEditor class
21918 * Create a new HtmlEditor
21919 * @param {Object} config The config object
21922 Roo.bootstrap.HtmlEditor = function(config){
21923 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21924 if (!this.toolbars) {
21925 this.toolbars = [];
21927 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21930 * @event initialize
21931 * Fires when the editor is fully initialized (including the iframe)
21932 * @param {HtmlEditor} this
21937 * Fires when the editor is first receives the focus. Any insertion must wait
21938 * until after this event.
21939 * @param {HtmlEditor} this
21943 * @event beforesync
21944 * Fires before the textarea is updated with content from the editor iframe. Return false
21945 * to cancel the sync.
21946 * @param {HtmlEditor} this
21947 * @param {String} html
21951 * @event beforepush
21952 * Fires before the iframe editor is updated with content from the textarea. Return false
21953 * to cancel the push.
21954 * @param {HtmlEditor} this
21955 * @param {String} html
21960 * Fires when the textarea is updated with content from the editor iframe.
21961 * @param {HtmlEditor} this
21962 * @param {String} html
21967 * Fires when the iframe editor is updated with content from the textarea.
21968 * @param {HtmlEditor} this
21969 * @param {String} html
21973 * @event editmodechange
21974 * Fires when the editor switches edit modes
21975 * @param {HtmlEditor} this
21976 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21978 editmodechange: true,
21980 * @event editorevent
21981 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21982 * @param {HtmlEditor} this
21986 * @event firstfocus
21987 * Fires when on first focus - needed by toolbars..
21988 * @param {HtmlEditor} this
21993 * Auto save the htmlEditor value as a file into Events
21994 * @param {HtmlEditor} this
21998 * @event savedpreview
21999 * preview the saved version of htmlEditor
22000 * @param {HtmlEditor} this
22007 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22011 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22016 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22021 * @cfg {Number} height (in pixels)
22025 * @cfg {Number} width (in pixels)
22030 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22033 stylesheets: false,
22038 // private properties
22039 validationEvent : false,
22041 initialized : false,
22044 onFocus : Roo.emptyFn,
22046 hideMode:'offsets',
22049 tbContainer : false,
22051 toolbarContainer :function() {
22052 return this.wrap.select('.x-html-editor-tb',true).first();
22056 * Protected method that will not generally be called directly. It
22057 * is called when the editor creates its toolbar. Override this method if you need to
22058 * add custom toolbar buttons.
22059 * @param {HtmlEditor} editor
22061 createToolbar : function(){
22063 Roo.log("create toolbars");
22065 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22066 this.toolbars[0].render(this.toolbarContainer());
22070 // if (!editor.toolbars || !editor.toolbars.length) {
22071 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22074 // for (var i =0 ; i < editor.toolbars.length;i++) {
22075 // editor.toolbars[i] = Roo.factory(
22076 // typeof(editor.toolbars[i]) == 'string' ?
22077 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22078 // Roo.bootstrap.HtmlEditor);
22079 // editor.toolbars[i].init(editor);
22085 onRender : function(ct, position)
22087 // Roo.log("Call onRender: " + this.xtype);
22089 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22091 this.wrap = this.inputEl().wrap({
22092 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22095 this.editorcore.onRender(ct, position);
22097 if (this.resizable) {
22098 this.resizeEl = new Roo.Resizable(this.wrap, {
22102 minHeight : this.height,
22103 height: this.height,
22104 handles : this.resizable,
22107 resize : function(r, w, h) {
22108 _t.onResize(w,h); // -something
22114 this.createToolbar(this);
22117 if(!this.width && this.resizable){
22118 this.setSize(this.wrap.getSize());
22120 if (this.resizeEl) {
22121 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22122 // should trigger onReize..
22128 onResize : function(w, h)
22130 Roo.log('resize: ' +w + ',' + h );
22131 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22135 if(this.inputEl() ){
22136 if(typeof w == 'number'){
22137 var aw = w - this.wrap.getFrameWidth('lr');
22138 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22141 if(typeof h == 'number'){
22142 var tbh = -11; // fixme it needs to tool bar size!
22143 for (var i =0; i < this.toolbars.length;i++) {
22144 // fixme - ask toolbars for heights?
22145 tbh += this.toolbars[i].el.getHeight();
22146 //if (this.toolbars[i].footer) {
22147 // tbh += this.toolbars[i].footer.el.getHeight();
22155 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22156 ah -= 5; // knock a few pixes off for look..
22157 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22161 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22162 this.editorcore.onResize(ew,eh);
22167 * Toggles the editor between standard and source edit mode.
22168 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22170 toggleSourceEdit : function(sourceEditMode)
22172 this.editorcore.toggleSourceEdit(sourceEditMode);
22174 if(this.editorcore.sourceEditMode){
22175 Roo.log('editor - showing textarea');
22178 // Roo.log(this.syncValue());
22180 this.inputEl().removeClass(['hide', 'x-hidden']);
22181 this.inputEl().dom.removeAttribute('tabIndex');
22182 this.inputEl().focus();
22184 Roo.log('editor - hiding textarea');
22186 // Roo.log(this.pushValue());
22189 this.inputEl().addClass(['hide', 'x-hidden']);
22190 this.inputEl().dom.setAttribute('tabIndex', -1);
22191 //this.deferFocus();
22194 if(this.resizable){
22195 this.setSize(this.wrap.getSize());
22198 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22201 // private (for BoxComponent)
22202 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22204 // private (for BoxComponent)
22205 getResizeEl : function(){
22209 // private (for BoxComponent)
22210 getPositionEl : function(){
22215 initEvents : function(){
22216 this.originalValue = this.getValue();
22220 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22223 // markInvalid : Roo.emptyFn,
22225 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22228 // clearInvalid : Roo.emptyFn,
22230 setValue : function(v){
22231 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22232 this.editorcore.pushValue();
22237 deferFocus : function(){
22238 this.focus.defer(10, this);
22242 focus : function(){
22243 this.editorcore.focus();
22249 onDestroy : function(){
22255 for (var i =0; i < this.toolbars.length;i++) {
22256 // fixme - ask toolbars for heights?
22257 this.toolbars[i].onDestroy();
22260 this.wrap.dom.innerHTML = '';
22261 this.wrap.remove();
22266 onFirstFocus : function(){
22267 //Roo.log("onFirstFocus");
22268 this.editorcore.onFirstFocus();
22269 for (var i =0; i < this.toolbars.length;i++) {
22270 this.toolbars[i].onFirstFocus();
22276 syncValue : function()
22278 this.editorcore.syncValue();
22281 pushValue : function()
22283 this.editorcore.pushValue();
22287 // hide stuff that is not compatible
22301 * @event specialkey
22305 * @cfg {String} fieldClass @hide
22308 * @cfg {String} focusClass @hide
22311 * @cfg {String} autoCreate @hide
22314 * @cfg {String} inputType @hide
22317 * @cfg {String} invalidClass @hide
22320 * @cfg {String} invalidText @hide
22323 * @cfg {String} msgFx @hide
22326 * @cfg {String} validateOnBlur @hide
22335 Roo.namespace('Roo.bootstrap.htmleditor');
22337 * @class Roo.bootstrap.HtmlEditorToolbar1
22342 new Roo.bootstrap.HtmlEditor({
22345 new Roo.bootstrap.HtmlEditorToolbar1({
22346 disable : { fonts: 1 , format: 1, ..., ... , ...],
22352 * @cfg {Object} disable List of elements to disable..
22353 * @cfg {Array} btns List of additional buttons.
22357 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22360 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22363 Roo.apply(this, config);
22365 // default disabled, based on 'good practice'..
22366 this.disable = this.disable || {};
22367 Roo.applyIf(this.disable, {
22370 specialElements : true
22372 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22374 this.editor = config.editor;
22375 this.editorcore = config.editor.editorcore;
22377 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22379 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22380 // dont call parent... till later.
22382 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22387 editorcore : false,
22392 "h1","h2","h3","h4","h5","h6",
22394 "abbr", "acronym", "address", "cite", "samp", "var",
22398 onRender : function(ct, position)
22400 // Roo.log("Call onRender: " + this.xtype);
22402 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22404 this.el.dom.style.marginBottom = '0';
22406 var editorcore = this.editorcore;
22407 var editor= this.editor;
22410 var btn = function(id,cmd , toggle, handler){
22412 var event = toggle ? 'toggle' : 'click';
22417 xns: Roo.bootstrap,
22420 enableToggle:toggle !== false,
22422 pressed : toggle ? false : null,
22425 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22426 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22435 xns: Roo.bootstrap,
22436 glyphicon : 'font',
22440 xns: Roo.bootstrap,
22444 Roo.each(this.formats, function(f) {
22445 style.menu.items.push({
22447 xns: Roo.bootstrap,
22448 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22453 editorcore.insertTag(this.tagname);
22460 children.push(style);
22463 btn('bold',false,true);
22464 btn('italic',false,true);
22465 btn('align-left', 'justifyleft',true);
22466 btn('align-center', 'justifycenter',true);
22467 btn('align-right' , 'justifyright',true);
22468 btn('link', false, false, function(btn) {
22469 //Roo.log("create link?");
22470 var url = prompt(this.createLinkText, this.defaultLinkValue);
22471 if(url && url != 'http:/'+'/'){
22472 this.editorcore.relayCmd('createlink', url);
22475 btn('list','insertunorderedlist',true);
22476 btn('pencil', false,true, function(btn){
22479 this.toggleSourceEdit(btn.pressed);
22485 xns: Roo.bootstrap,
22490 xns: Roo.bootstrap,
22495 cog.menu.items.push({
22497 xns: Roo.bootstrap,
22498 html : Clean styles,
22503 editorcore.insertTag(this.tagname);
22512 this.xtype = 'NavSimplebar';
22514 for(var i=0;i< children.length;i++) {
22516 this.buttons.add(this.addxtypeChild(children[i]));
22520 editor.on('editorevent', this.updateToolbar, this);
22522 onBtnClick : function(id)
22524 this.editorcore.relayCmd(id);
22525 this.editorcore.focus();
22529 * Protected method that will not generally be called directly. It triggers
22530 * a toolbar update by reading the markup state of the current selection in the editor.
22532 updateToolbar: function(){
22534 if(!this.editorcore.activated){
22535 this.editor.onFirstFocus(); // is this neeed?
22539 var btns = this.buttons;
22540 var doc = this.editorcore.doc;
22541 btns.get('bold').setActive(doc.queryCommandState('bold'));
22542 btns.get('italic').setActive(doc.queryCommandState('italic'));
22543 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22545 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22546 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22547 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22549 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22550 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22553 var ans = this.editorcore.getAllAncestors();
22554 if (this.formatCombo) {
22557 var store = this.formatCombo.store;
22558 this.formatCombo.setValue("");
22559 for (var i =0; i < ans.length;i++) {
22560 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22562 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22570 // hides menus... - so this cant be on a menu...
22571 Roo.bootstrap.MenuMgr.hideAll();
22573 Roo.bootstrap.MenuMgr.hideAll();
22574 //this.editorsyncValue();
22576 onFirstFocus: function() {
22577 this.buttons.each(function(item){
22581 toggleSourceEdit : function(sourceEditMode){
22584 if(sourceEditMode){
22585 Roo.log("disabling buttons");
22586 this.buttons.each( function(item){
22587 if(item.cmd != 'pencil'){
22593 Roo.log("enabling buttons");
22594 if(this.editorcore.initialized){
22595 this.buttons.each( function(item){
22601 Roo.log("calling toggole on editor");
22602 // tell the editor that it's been pressed..
22603 this.editor.toggleSourceEdit(sourceEditMode);
22613 * @class Roo.bootstrap.Table.AbstractSelectionModel
22614 * @extends Roo.util.Observable
22615 * Abstract base class for grid SelectionModels. It provides the interface that should be
22616 * implemented by descendant classes. This class should not be directly instantiated.
22619 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22620 this.locked = false;
22621 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22625 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22626 /** @ignore Called by the grid automatically. Do not call directly. */
22627 init : function(grid){
22633 * Locks the selections.
22636 this.locked = true;
22640 * Unlocks the selections.
22642 unlock : function(){
22643 this.locked = false;
22647 * Returns true if the selections are locked.
22648 * @return {Boolean}
22650 isLocked : function(){
22651 return this.locked;
22655 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22656 * @class Roo.bootstrap.Table.RowSelectionModel
22657 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22658 * It supports multiple selections and keyboard selection/navigation.
22660 * @param {Object} config
22663 Roo.bootstrap.Table.RowSelectionModel = function(config){
22664 Roo.apply(this, config);
22665 this.selections = new Roo.util.MixedCollection(false, function(o){
22670 this.lastActive = false;
22674 * @event selectionchange
22675 * Fires when the selection changes
22676 * @param {SelectionModel} this
22678 "selectionchange" : true,
22680 * @event afterselectionchange
22681 * Fires after the selection changes (eg. by key press or clicking)
22682 * @param {SelectionModel} this
22684 "afterselectionchange" : true,
22686 * @event beforerowselect
22687 * Fires when a row is selected being selected, return false to cancel.
22688 * @param {SelectionModel} this
22689 * @param {Number} rowIndex The selected index
22690 * @param {Boolean} keepExisting False if other selections will be cleared
22692 "beforerowselect" : true,
22695 * Fires when a row is selected.
22696 * @param {SelectionModel} this
22697 * @param {Number} rowIndex The selected index
22698 * @param {Roo.data.Record} r The record
22700 "rowselect" : true,
22702 * @event rowdeselect
22703 * Fires when a row is deselected.
22704 * @param {SelectionModel} this
22705 * @param {Number} rowIndex The selected index
22707 "rowdeselect" : true
22709 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22710 this.locked = false;
22713 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22715 * @cfg {Boolean} singleSelect
22716 * True to allow selection of only one row at a time (defaults to false)
22718 singleSelect : false,
22721 initEvents : function()
22724 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22725 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22726 //}else{ // allow click to work like normal
22727 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22729 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22730 this.grid.on("rowclick", this.handleMouseDown, this);
22732 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22733 "up" : function(e){
22735 this.selectPrevious(e.shiftKey);
22736 }else if(this.last !== false && this.lastActive !== false){
22737 var last = this.last;
22738 this.selectRange(this.last, this.lastActive-1);
22739 this.grid.getView().focusRow(this.lastActive);
22740 if(last !== false){
22744 this.selectFirstRow();
22746 this.fireEvent("afterselectionchange", this);
22748 "down" : function(e){
22750 this.selectNext(e.shiftKey);
22751 }else if(this.last !== false && this.lastActive !== false){
22752 var last = this.last;
22753 this.selectRange(this.last, this.lastActive+1);
22754 this.grid.getView().focusRow(this.lastActive);
22755 if(last !== false){
22759 this.selectFirstRow();
22761 this.fireEvent("afterselectionchange", this);
22765 this.grid.store.on('load', function(){
22766 this.selections.clear();
22769 var view = this.grid.view;
22770 view.on("refresh", this.onRefresh, this);
22771 view.on("rowupdated", this.onRowUpdated, this);
22772 view.on("rowremoved", this.onRemove, this);
22777 onRefresh : function()
22779 var ds = this.grid.store, i, v = this.grid.view;
22780 var s = this.selections;
22781 s.each(function(r){
22782 if((i = ds.indexOfId(r.id)) != -1){
22791 onRemove : function(v, index, r){
22792 this.selections.remove(r);
22796 onRowUpdated : function(v, index, r){
22797 if(this.isSelected(r)){
22798 v.onRowSelect(index);
22804 * @param {Array} records The records to select
22805 * @param {Boolean} keepExisting (optional) True to keep existing selections
22807 selectRecords : function(records, keepExisting)
22810 this.clearSelections();
22812 var ds = this.grid.store;
22813 for(var i = 0, len = records.length; i < len; i++){
22814 this.selectRow(ds.indexOf(records[i]), true);
22819 * Gets the number of selected rows.
22822 getCount : function(){
22823 return this.selections.length;
22827 * Selects the first row in the grid.
22829 selectFirstRow : function(){
22834 * Select the last row.
22835 * @param {Boolean} keepExisting (optional) True to keep existing selections
22837 selectLastRow : function(keepExisting){
22838 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22839 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22843 * Selects the row immediately following the last selected row.
22844 * @param {Boolean} keepExisting (optional) True to keep existing selections
22846 selectNext : function(keepExisting)
22848 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22849 this.selectRow(this.last+1, keepExisting);
22850 this.grid.getView().focusRow(this.last);
22855 * Selects the row that precedes the last selected row.
22856 * @param {Boolean} keepExisting (optional) True to keep existing selections
22858 selectPrevious : function(keepExisting){
22860 this.selectRow(this.last-1, keepExisting);
22861 this.grid.getView().focusRow(this.last);
22866 * Returns the selected records
22867 * @return {Array} Array of selected records
22869 getSelections : function(){
22870 return [].concat(this.selections.items);
22874 * Returns the first selected record.
22877 getSelected : function(){
22878 return this.selections.itemAt(0);
22883 * Clears all selections.
22885 clearSelections : function(fast)
22891 var ds = this.grid.store;
22892 var s = this.selections;
22893 s.each(function(r){
22894 this.deselectRow(ds.indexOfId(r.id));
22898 this.selections.clear();
22905 * Selects all rows.
22907 selectAll : function(){
22911 this.selections.clear();
22912 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22913 this.selectRow(i, true);
22918 * Returns True if there is a selection.
22919 * @return {Boolean}
22921 hasSelection : function(){
22922 return this.selections.length > 0;
22926 * Returns True if the specified row is selected.
22927 * @param {Number/Record} record The record or index of the record to check
22928 * @return {Boolean}
22930 isSelected : function(index){
22931 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22932 return (r && this.selections.key(r.id) ? true : false);
22936 * Returns True if the specified record id is selected.
22937 * @param {String} id The id of record to check
22938 * @return {Boolean}
22940 isIdSelected : function(id){
22941 return (this.selections.key(id) ? true : false);
22946 handleMouseDBClick : function(e, t){
22950 handleMouseDown : function(e, t)
22952 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22953 if(this.isLocked() || rowIndex < 0 ){
22956 if(e.shiftKey && this.last !== false){
22957 var last = this.last;
22958 this.selectRange(last, rowIndex, e.ctrlKey);
22959 this.last = last; // reset the last
22963 var isSelected = this.isSelected(rowIndex);
22964 //Roo.log("select row:" + rowIndex);
22966 this.deselectRow(rowIndex);
22968 this.selectRow(rowIndex, true);
22972 if(e.button !== 0 && isSelected){
22973 alert('rowIndex 2: ' + rowIndex);
22974 view.focusRow(rowIndex);
22975 }else if(e.ctrlKey && isSelected){
22976 this.deselectRow(rowIndex);
22977 }else if(!isSelected){
22978 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22979 view.focusRow(rowIndex);
22983 this.fireEvent("afterselectionchange", this);
22986 handleDragableRowClick : function(grid, rowIndex, e)
22988 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22989 this.selectRow(rowIndex, false);
22990 grid.view.focusRow(rowIndex);
22991 this.fireEvent("afterselectionchange", this);
22996 * Selects multiple rows.
22997 * @param {Array} rows Array of the indexes of the row to select
22998 * @param {Boolean} keepExisting (optional) True to keep existing selections
23000 selectRows : function(rows, keepExisting){
23002 this.clearSelections();
23004 for(var i = 0, len = rows.length; i < len; i++){
23005 this.selectRow(rows[i], true);
23010 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23011 * @param {Number} startRow The index of the first row in the range
23012 * @param {Number} endRow The index of the last row in the range
23013 * @param {Boolean} keepExisting (optional) True to retain existing selections
23015 selectRange : function(startRow, endRow, keepExisting){
23020 this.clearSelections();
23022 if(startRow <= endRow){
23023 for(var i = startRow; i <= endRow; i++){
23024 this.selectRow(i, true);
23027 for(var i = startRow; i >= endRow; i--){
23028 this.selectRow(i, true);
23034 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23035 * @param {Number} startRow The index of the first row in the range
23036 * @param {Number} endRow The index of the last row in the range
23038 deselectRange : function(startRow, endRow, preventViewNotify){
23042 for(var i = startRow; i <= endRow; i++){
23043 this.deselectRow(i, preventViewNotify);
23049 * @param {Number} row The index of the row to select
23050 * @param {Boolean} keepExisting (optional) True to keep existing selections
23052 selectRow : function(index, keepExisting, preventViewNotify)
23054 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23057 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23058 if(!keepExisting || this.singleSelect){
23059 this.clearSelections();
23062 var r = this.grid.store.getAt(index);
23063 //console.log('selectRow - record id :' + r.id);
23065 this.selections.add(r);
23066 this.last = this.lastActive = index;
23067 if(!preventViewNotify){
23068 var proxy = new Roo.Element(
23069 this.grid.getRowDom(index)
23071 proxy.addClass('bg-info info');
23073 this.fireEvent("rowselect", this, index, r);
23074 this.fireEvent("selectionchange", this);
23080 * @param {Number} row The index of the row to deselect
23082 deselectRow : function(index, preventViewNotify)
23087 if(this.last == index){
23090 if(this.lastActive == index){
23091 this.lastActive = false;
23094 var r = this.grid.store.getAt(index);
23099 this.selections.remove(r);
23100 //.console.log('deselectRow - record id :' + r.id);
23101 if(!preventViewNotify){
23103 var proxy = new Roo.Element(
23104 this.grid.getRowDom(index)
23106 proxy.removeClass('bg-info info');
23108 this.fireEvent("rowdeselect", this, index);
23109 this.fireEvent("selectionchange", this);
23113 restoreLast : function(){
23115 this.last = this._last;
23120 acceptsNav : function(row, col, cm){
23121 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23125 onEditorKey : function(field, e){
23126 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23131 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23133 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23135 }else if(k == e.ENTER && !e.ctrlKey){
23139 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23141 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23143 }else if(k == e.ESC){
23147 g.startEditing(newCell[0], newCell[1]);
23153 * Ext JS Library 1.1.1
23154 * Copyright(c) 2006-2007, Ext JS, LLC.
23156 * Originally Released Under LGPL - original licence link has changed is not relivant.
23159 * <script type="text/javascript">
23163 * @class Roo.bootstrap.PagingToolbar
23164 * @extends Roo.bootstrap.NavSimplebar
23165 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23167 * Create a new PagingToolbar
23168 * @param {Object} config The config object
23169 * @param {Roo.data.Store} store
23171 Roo.bootstrap.PagingToolbar = function(config)
23173 // old args format still supported... - xtype is prefered..
23174 // created from xtype...
23176 this.ds = config.dataSource;
23178 if (config.store && !this.ds) {
23179 this.store= Roo.factory(config.store, Roo.data);
23180 this.ds = this.store;
23181 this.ds.xmodule = this.xmodule || false;
23184 this.toolbarItems = [];
23185 if (config.items) {
23186 this.toolbarItems = config.items;
23189 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23194 this.bind(this.ds);
23197 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23201 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23203 * @cfg {Roo.data.Store} dataSource
23204 * The underlying data store providing the paged data
23207 * @cfg {String/HTMLElement/Element} container
23208 * container The id or element that will contain the toolbar
23211 * @cfg {Boolean} displayInfo
23212 * True to display the displayMsg (defaults to false)
23215 * @cfg {Number} pageSize
23216 * The number of records to display per page (defaults to 20)
23220 * @cfg {String} displayMsg
23221 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23223 displayMsg : 'Displaying {0} - {1} of {2}',
23225 * @cfg {String} emptyMsg
23226 * The message to display when no records are found (defaults to "No data to display")
23228 emptyMsg : 'No data to display',
23230 * Customizable piece of the default paging text (defaults to "Page")
23233 beforePageText : "Page",
23235 * Customizable piece of the default paging text (defaults to "of %0")
23238 afterPageText : "of {0}",
23240 * Customizable piece of the default paging text (defaults to "First Page")
23243 firstText : "First Page",
23245 * Customizable piece of the default paging text (defaults to "Previous Page")
23248 prevText : "Previous Page",
23250 * Customizable piece of the default paging text (defaults to "Next Page")
23253 nextText : "Next Page",
23255 * Customizable piece of the default paging text (defaults to "Last Page")
23258 lastText : "Last Page",
23260 * Customizable piece of the default paging text (defaults to "Refresh")
23263 refreshText : "Refresh",
23267 onRender : function(ct, position)
23269 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23270 this.navgroup.parentId = this.id;
23271 this.navgroup.onRender(this.el, null);
23272 // add the buttons to the navgroup
23274 if(this.displayInfo){
23275 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23276 this.displayEl = this.el.select('.x-paging-info', true).first();
23277 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23278 // this.displayEl = navel.el.select('span',true).first();
23284 Roo.each(_this.buttons, function(e){ // this might need to use render????
23285 Roo.factory(e).onRender(_this.el, null);
23289 Roo.each(_this.toolbarItems, function(e) {
23290 _this.navgroup.addItem(e);
23294 this.first = this.navgroup.addItem({
23295 tooltip: this.firstText,
23297 icon : 'fa fa-backward',
23299 preventDefault: true,
23300 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23303 this.prev = this.navgroup.addItem({
23304 tooltip: this.prevText,
23306 icon : 'fa fa-step-backward',
23308 preventDefault: true,
23309 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23311 //this.addSeparator();
23314 var field = this.navgroup.addItem( {
23316 cls : 'x-paging-position',
23318 html : this.beforePageText +
23319 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23320 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23323 this.field = field.el.select('input', true).first();
23324 this.field.on("keydown", this.onPagingKeydown, this);
23325 this.field.on("focus", function(){this.dom.select();});
23328 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23329 //this.field.setHeight(18);
23330 //this.addSeparator();
23331 this.next = this.navgroup.addItem({
23332 tooltip: this.nextText,
23334 html : ' <i class="fa fa-step-forward">',
23336 preventDefault: true,
23337 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23339 this.last = this.navgroup.addItem({
23340 tooltip: this.lastText,
23341 icon : 'fa fa-forward',
23344 preventDefault: true,
23345 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23347 //this.addSeparator();
23348 this.loading = this.navgroup.addItem({
23349 tooltip: this.refreshText,
23350 icon: 'fa fa-refresh',
23351 preventDefault: true,
23352 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23358 updateInfo : function(){
23359 if(this.displayEl){
23360 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23361 var msg = count == 0 ?
23365 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23367 this.displayEl.update(msg);
23372 onLoad : function(ds, r, o){
23373 this.cursor = o.params ? o.params.start : 0;
23374 var d = this.getPageData(),
23378 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23379 this.field.dom.value = ap;
23380 this.first.setDisabled(ap == 1);
23381 this.prev.setDisabled(ap == 1);
23382 this.next.setDisabled(ap == ps);
23383 this.last.setDisabled(ap == ps);
23384 this.loading.enable();
23389 getPageData : function(){
23390 var total = this.ds.getTotalCount();
23393 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23394 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23399 onLoadError : function(){
23400 this.loading.enable();
23404 onPagingKeydown : function(e){
23405 var k = e.getKey();
23406 var d = this.getPageData();
23408 var v = this.field.dom.value, pageNum;
23409 if(!v || isNaN(pageNum = parseInt(v, 10))){
23410 this.field.dom.value = d.activePage;
23413 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23414 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23417 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))
23419 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23420 this.field.dom.value = pageNum;
23421 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23424 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23426 var v = this.field.dom.value, pageNum;
23427 var increment = (e.shiftKey) ? 10 : 1;
23428 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23431 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23432 this.field.dom.value = d.activePage;
23435 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23437 this.field.dom.value = parseInt(v, 10) + increment;
23438 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23439 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23446 beforeLoad : function(){
23448 this.loading.disable();
23453 onClick : function(which){
23462 ds.load({params:{start: 0, limit: this.pageSize}});
23465 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23468 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23471 var total = ds.getTotalCount();
23472 var extra = total % this.pageSize;
23473 var lastStart = extra ? (total - extra) : total-this.pageSize;
23474 ds.load({params:{start: lastStart, limit: this.pageSize}});
23477 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23483 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23484 * @param {Roo.data.Store} store The data store to unbind
23486 unbind : function(ds){
23487 ds.un("beforeload", this.beforeLoad, this);
23488 ds.un("load", this.onLoad, this);
23489 ds.un("loadexception", this.onLoadError, this);
23490 ds.un("remove", this.updateInfo, this);
23491 ds.un("add", this.updateInfo, this);
23492 this.ds = undefined;
23496 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23497 * @param {Roo.data.Store} store The data store to bind
23499 bind : function(ds){
23500 ds.on("beforeload", this.beforeLoad, this);
23501 ds.on("load", this.onLoad, this);
23502 ds.on("loadexception", this.onLoadError, this);
23503 ds.on("remove", this.updateInfo, this);
23504 ds.on("add", this.updateInfo, this);
23515 * @class Roo.bootstrap.MessageBar
23516 * @extends Roo.bootstrap.Component
23517 * Bootstrap MessageBar class
23518 * @cfg {String} html contents of the MessageBar
23519 * @cfg {String} weight (info | success | warning | danger) default info
23520 * @cfg {String} beforeClass insert the bar before the given class
23521 * @cfg {Boolean} closable (true | false) default false
23522 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23525 * Create a new Element
23526 * @param {Object} config The config object
23529 Roo.bootstrap.MessageBar = function(config){
23530 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23533 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23539 beforeClass: 'bootstrap-sticky-wrap',
23541 getAutoCreate : function(){
23545 cls: 'alert alert-dismissable alert-' + this.weight,
23550 html: this.html || ''
23556 cfg.cls += ' alert-messages-fixed';
23570 onRender : function(ct, position)
23572 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23575 var cfg = Roo.apply({}, this.getAutoCreate());
23579 cfg.cls += ' ' + this.cls;
23582 cfg.style = this.style;
23584 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23586 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23589 this.el.select('>button.close').on('click', this.hide, this);
23595 if (!this.rendered) {
23601 this.fireEvent('show', this);
23607 if (!this.rendered) {
23613 this.fireEvent('hide', this);
23616 update : function()
23618 // var e = this.el.dom.firstChild;
23620 // if(this.closable){
23621 // e = e.nextSibling;
23624 // e.data = this.html || '';
23626 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23642 * @class Roo.bootstrap.Graph
23643 * @extends Roo.bootstrap.Component
23644 * Bootstrap Graph class
23648 @cfg {String} graphtype bar | vbar | pie
23649 @cfg {number} g_x coodinator | centre x (pie)
23650 @cfg {number} g_y coodinator | centre y (pie)
23651 @cfg {number} g_r radius (pie)
23652 @cfg {number} g_height height of the chart (respected by all elements in the set)
23653 @cfg {number} g_width width of the chart (respected by all elements in the set)
23654 @cfg {Object} title The title of the chart
23657 -opts (object) options for the chart
23659 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23660 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23662 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.
23663 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23665 o stretch (boolean)
23667 -opts (object) options for the pie
23670 o startAngle (number)
23671 o endAngle (number)
23675 * Create a new Input
23676 * @param {Object} config The config object
23679 Roo.bootstrap.Graph = function(config){
23680 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23686 * The img click event for the img.
23687 * @param {Roo.EventObject} e
23693 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23704 //g_colors: this.colors,
23711 getAutoCreate : function(){
23722 onRender : function(ct,position){
23725 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23727 if (typeof(Raphael) == 'undefined') {
23728 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23732 this.raphael = Raphael(this.el.dom);
23734 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23735 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23736 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23737 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23739 r.text(160, 10, "Single Series Chart").attr(txtattr);
23740 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23741 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23742 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23744 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23745 r.barchart(330, 10, 300, 220, data1);
23746 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23747 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23750 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23751 // r.barchart(30, 30, 560, 250, xdata, {
23752 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23753 // axis : "0 0 1 1",
23754 // axisxlabels : xdata
23755 // //yvalues : cols,
23758 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23760 // this.load(null,xdata,{
23761 // axis : "0 0 1 1",
23762 // axisxlabels : xdata
23767 load : function(graphtype,xdata,opts)
23769 this.raphael.clear();
23771 graphtype = this.graphtype;
23776 var r = this.raphael,
23777 fin = function () {
23778 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23780 fout = function () {
23781 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23783 pfin = function() {
23784 this.sector.stop();
23785 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23788 this.label[0].stop();
23789 this.label[0].attr({ r: 7.5 });
23790 this.label[1].attr({ "font-weight": 800 });
23793 pfout = function() {
23794 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23797 this.label[0].animate({ r: 5 }, 500, "bounce");
23798 this.label[1].attr({ "font-weight": 400 });
23804 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23807 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23810 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23811 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23813 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23820 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23825 setTitle: function(o)
23830 initEvents: function() {
23833 this.el.on('click', this.onClick, this);
23837 onClick : function(e)
23839 Roo.log('img onclick');
23840 this.fireEvent('click', this, e);
23852 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23855 * @class Roo.bootstrap.dash.NumberBox
23856 * @extends Roo.bootstrap.Component
23857 * Bootstrap NumberBox class
23858 * @cfg {String} headline Box headline
23859 * @cfg {String} content Box content
23860 * @cfg {String} icon Box icon
23861 * @cfg {String} footer Footer text
23862 * @cfg {String} fhref Footer href
23865 * Create a new NumberBox
23866 * @param {Object} config The config object
23870 Roo.bootstrap.dash.NumberBox = function(config){
23871 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23875 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23884 getAutoCreate : function(){
23888 cls : 'small-box ',
23896 cls : 'roo-headline',
23897 html : this.headline
23901 cls : 'roo-content',
23902 html : this.content
23916 cls : 'ion ' + this.icon
23925 cls : 'small-box-footer',
23926 href : this.fhref || '#',
23930 cfg.cn.push(footer);
23937 onRender : function(ct,position){
23938 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23945 setHeadline: function (value)
23947 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23950 setFooter: function (value, href)
23952 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23955 this.el.select('a.small-box-footer',true).first().attr('href', href);
23960 setContent: function (value)
23962 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23965 initEvents: function()
23979 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23982 * @class Roo.bootstrap.dash.TabBox
23983 * @extends Roo.bootstrap.Component
23984 * Bootstrap TabBox class
23985 * @cfg {String} title Title of the TabBox
23986 * @cfg {String} icon Icon of the TabBox
23987 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23988 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23991 * Create a new TabBox
23992 * @param {Object} config The config object
23996 Roo.bootstrap.dash.TabBox = function(config){
23997 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24002 * When a pane is added
24003 * @param {Roo.bootstrap.dash.TabPane} pane
24007 * @event activatepane
24008 * When a pane is activated
24009 * @param {Roo.bootstrap.dash.TabPane} pane
24011 "activatepane" : true
24019 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24024 tabScrollable : false,
24026 getChildContainer : function()
24028 return this.el.select('.tab-content', true).first();
24031 getAutoCreate : function(){
24035 cls: 'pull-left header',
24043 cls: 'fa ' + this.icon
24049 cls: 'nav nav-tabs pull-right',
24055 if(this.tabScrollable){
24062 cls: 'nav nav-tabs pull-right',
24073 cls: 'nav-tabs-custom',
24078 cls: 'tab-content no-padding',
24086 initEvents : function()
24088 //Roo.log('add add pane handler');
24089 this.on('addpane', this.onAddPane, this);
24092 * Updates the box title
24093 * @param {String} html to set the title to.
24095 setTitle : function(value)
24097 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24099 onAddPane : function(pane)
24101 this.panes.push(pane);
24102 //Roo.log('addpane');
24104 // tabs are rendere left to right..
24105 if(!this.showtabs){
24109 var ctr = this.el.select('.nav-tabs', true).first();
24112 var existing = ctr.select('.nav-tab',true);
24113 var qty = existing.getCount();;
24116 var tab = ctr.createChild({
24118 cls : 'nav-tab' + (qty ? '' : ' active'),
24126 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24129 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24131 pane.el.addClass('active');
24136 onTabClick : function(ev,un,ob,pane)
24138 //Roo.log('tab - prev default');
24139 ev.preventDefault();
24142 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24143 pane.tab.addClass('active');
24144 //Roo.log(pane.title);
24145 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24146 // technically we should have a deactivate event.. but maybe add later.
24147 // and it should not de-activate the selected tab...
24148 this.fireEvent('activatepane', pane);
24149 pane.el.addClass('active');
24150 pane.fireEvent('activate');
24155 getActivePane : function()
24158 Roo.each(this.panes, function(p) {
24159 if(p.el.hasClass('active')){
24180 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24182 * @class Roo.bootstrap.TabPane
24183 * @extends Roo.bootstrap.Component
24184 * Bootstrap TabPane class
24185 * @cfg {Boolean} active (false | true) Default false
24186 * @cfg {String} title title of panel
24190 * Create a new TabPane
24191 * @param {Object} config The config object
24194 Roo.bootstrap.dash.TabPane = function(config){
24195 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24201 * When a pane is activated
24202 * @param {Roo.bootstrap.dash.TabPane} pane
24209 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24214 // the tabBox that this is attached to.
24217 getAutoCreate : function()
24225 cfg.cls += ' active';
24230 initEvents : function()
24232 //Roo.log('trigger add pane handler');
24233 this.parent().fireEvent('addpane', this)
24237 * Updates the tab title
24238 * @param {String} html to set the title to.
24240 setTitle: function(str)
24246 this.tab.select('a', true).first().dom.innerHTML = str;
24263 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24266 * @class Roo.bootstrap.menu.Menu
24267 * @extends Roo.bootstrap.Component
24268 * Bootstrap Menu class - container for Menu
24269 * @cfg {String} html Text of the menu
24270 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24271 * @cfg {String} icon Font awesome icon
24272 * @cfg {String} pos Menu align to (top | bottom) default bottom
24276 * Create a new Menu
24277 * @param {Object} config The config object
24281 Roo.bootstrap.menu.Menu = function(config){
24282 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24286 * @event beforeshow
24287 * Fires before this menu is displayed
24288 * @param {Roo.bootstrap.menu.Menu} this
24292 * @event beforehide
24293 * Fires before this menu is hidden
24294 * @param {Roo.bootstrap.menu.Menu} this
24299 * Fires after this menu is displayed
24300 * @param {Roo.bootstrap.menu.Menu} this
24305 * Fires after this menu is hidden
24306 * @param {Roo.bootstrap.menu.Menu} this
24311 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24312 * @param {Roo.bootstrap.menu.Menu} this
24313 * @param {Roo.EventObject} e
24320 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24324 weight : 'default',
24329 getChildContainer : function() {
24330 if(this.isSubMenu){
24334 return this.el.select('ul.dropdown-menu', true).first();
24337 getAutoCreate : function()
24342 cls : 'roo-menu-text',
24350 cls : 'fa ' + this.icon
24361 cls : 'dropdown-button btn btn-' + this.weight,
24366 cls : 'dropdown-toggle btn btn-' + this.weight,
24376 cls : 'dropdown-menu'
24382 if(this.pos == 'top'){
24383 cfg.cls += ' dropup';
24386 if(this.isSubMenu){
24389 cls : 'dropdown-menu'
24396 onRender : function(ct, position)
24398 this.isSubMenu = ct.hasClass('dropdown-submenu');
24400 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24403 initEvents : function()
24405 if(this.isSubMenu){
24409 this.hidden = true;
24411 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24412 this.triggerEl.on('click', this.onTriggerPress, this);
24414 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24415 this.buttonEl.on('click', this.onClick, this);
24421 if(this.isSubMenu){
24425 return this.el.select('ul.dropdown-menu', true).first();
24428 onClick : function(e)
24430 this.fireEvent("click", this, e);
24433 onTriggerPress : function(e)
24435 if (this.isVisible()) {
24442 isVisible : function(){
24443 return !this.hidden;
24448 this.fireEvent("beforeshow", this);
24450 this.hidden = false;
24451 this.el.addClass('open');
24453 Roo.get(document).on("mouseup", this.onMouseUp, this);
24455 this.fireEvent("show", this);
24462 this.fireEvent("beforehide", this);
24464 this.hidden = true;
24465 this.el.removeClass('open');
24467 Roo.get(document).un("mouseup", this.onMouseUp);
24469 this.fireEvent("hide", this);
24472 onMouseUp : function()
24486 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24489 * @class Roo.bootstrap.menu.Item
24490 * @extends Roo.bootstrap.Component
24491 * Bootstrap MenuItem class
24492 * @cfg {Boolean} submenu (true | false) default false
24493 * @cfg {String} html text of the item
24494 * @cfg {String} href the link
24495 * @cfg {Boolean} disable (true | false) default false
24496 * @cfg {Boolean} preventDefault (true | false) default true
24497 * @cfg {String} icon Font awesome icon
24498 * @cfg {String} pos Submenu align to (left | right) default right
24502 * Create a new Item
24503 * @param {Object} config The config object
24507 Roo.bootstrap.menu.Item = function(config){
24508 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24512 * Fires when the mouse is hovering over this menu
24513 * @param {Roo.bootstrap.menu.Item} this
24514 * @param {Roo.EventObject} e
24519 * Fires when the mouse exits this menu
24520 * @param {Roo.bootstrap.menu.Item} this
24521 * @param {Roo.EventObject} e
24527 * The raw click event for the entire grid.
24528 * @param {Roo.EventObject} e
24534 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24539 preventDefault: true,
24544 getAutoCreate : function()
24549 cls : 'roo-menu-item-text',
24557 cls : 'fa ' + this.icon
24566 href : this.href || '#',
24573 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24577 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24579 if(this.pos == 'left'){
24580 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24587 initEvents : function()
24589 this.el.on('mouseover', this.onMouseOver, this);
24590 this.el.on('mouseout', this.onMouseOut, this);
24592 this.el.select('a', true).first().on('click', this.onClick, this);
24596 onClick : function(e)
24598 if(this.preventDefault){
24599 e.preventDefault();
24602 this.fireEvent("click", this, e);
24605 onMouseOver : function(e)
24607 if(this.submenu && this.pos == 'left'){
24608 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24611 this.fireEvent("mouseover", this, e);
24614 onMouseOut : function(e)
24616 this.fireEvent("mouseout", this, e);
24628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24631 * @class Roo.bootstrap.menu.Separator
24632 * @extends Roo.bootstrap.Component
24633 * Bootstrap Separator class
24636 * Create a new Separator
24637 * @param {Object} config The config object
24641 Roo.bootstrap.menu.Separator = function(config){
24642 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24645 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24647 getAutoCreate : function(){
24668 * @class Roo.bootstrap.Tooltip
24669 * Bootstrap Tooltip class
24670 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24671 * to determine which dom element triggers the tooltip.
24673 * It needs to add support for additional attributes like tooltip-position
24676 * Create a new Toolti
24677 * @param {Object} config The config object
24680 Roo.bootstrap.Tooltip = function(config){
24681 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24684 Roo.apply(Roo.bootstrap.Tooltip, {
24686 * @function init initialize tooltip monitoring.
24690 currentTip : false,
24691 currentRegion : false,
24697 Roo.get(document).on('mouseover', this.enter ,this);
24698 Roo.get(document).on('mouseout', this.leave, this);
24701 this.currentTip = new Roo.bootstrap.Tooltip();
24704 enter : function(ev)
24706 var dom = ev.getTarget();
24708 //Roo.log(['enter',dom]);
24709 var el = Roo.fly(dom);
24710 if (this.currentEl) {
24712 //Roo.log(this.currentEl);
24713 //Roo.log(this.currentEl.contains(dom));
24714 if (this.currentEl == el) {
24717 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24723 if (this.currentTip.el) {
24724 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24728 if(!el || el.dom == document){
24734 // you can not look for children, as if el is the body.. then everythign is the child..
24735 if (!el.attr('tooltip')) { //
24736 if (!el.select("[tooltip]").elements.length) {
24739 // is the mouse over this child...?
24740 bindEl = el.select("[tooltip]").first();
24741 var xy = ev.getXY();
24742 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24743 //Roo.log("not in region.");
24746 //Roo.log("child element over..");
24749 this.currentEl = bindEl;
24750 this.currentTip.bind(bindEl);
24751 this.currentRegion = Roo.lib.Region.getRegion(dom);
24752 this.currentTip.enter();
24755 leave : function(ev)
24757 var dom = ev.getTarget();
24758 //Roo.log(['leave',dom]);
24759 if (!this.currentEl) {
24764 if (dom != this.currentEl.dom) {
24767 var xy = ev.getXY();
24768 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24771 // only activate leave if mouse cursor is outside... bounding box..
24776 if (this.currentTip) {
24777 this.currentTip.leave();
24779 //Roo.log('clear currentEl');
24780 this.currentEl = false;
24785 'left' : ['r-l', [-2,0], 'right'],
24786 'right' : ['l-r', [2,0], 'left'],
24787 'bottom' : ['t-b', [0,2], 'top'],
24788 'top' : [ 'b-t', [0,-2], 'bottom']
24794 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24799 delay : null, // can be { show : 300 , hide: 500}
24803 hoverState : null, //???
24805 placement : 'bottom',
24807 getAutoCreate : function(){
24814 cls : 'tooltip-arrow'
24817 cls : 'tooltip-inner'
24824 bind : function(el)
24830 enter : function () {
24832 if (this.timeout != null) {
24833 clearTimeout(this.timeout);
24836 this.hoverState = 'in';
24837 //Roo.log("enter - show");
24838 if (!this.delay || !this.delay.show) {
24843 this.timeout = setTimeout(function () {
24844 if (_t.hoverState == 'in') {
24847 }, this.delay.show);
24851 clearTimeout(this.timeout);
24853 this.hoverState = 'out';
24854 if (!this.delay || !this.delay.hide) {
24860 this.timeout = setTimeout(function () {
24861 //Roo.log("leave - timeout");
24863 if (_t.hoverState == 'out') {
24865 Roo.bootstrap.Tooltip.currentEl = false;
24873 this.render(document.body);
24876 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24878 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24880 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24882 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24884 var placement = typeof this.placement == 'function' ?
24885 this.placement.call(this, this.el, on_el) :
24888 var autoToken = /\s?auto?\s?/i;
24889 var autoPlace = autoToken.test(placement);
24891 placement = placement.replace(autoToken, '') || 'top';
24895 //this.el.setXY([0,0]);
24897 //this.el.dom.style.display='block';
24899 //this.el.appendTo(on_el);
24901 var p = this.getPosition();
24902 var box = this.el.getBox();
24908 var align = Roo.bootstrap.Tooltip.alignment[placement];
24910 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24912 if(placement == 'top' || placement == 'bottom'){
24914 placement = 'right';
24917 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24918 placement = 'left';
24921 var scroll = Roo.select('body', true).first().getScroll();
24923 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24929 align = Roo.bootstrap.Tooltip.alignment[placement];
24931 this.el.alignTo(this.bindEl, align[0],align[1]);
24932 //var arrow = this.el.select('.arrow',true).first();
24933 //arrow.set(align[2],
24935 this.el.addClass(placement);
24937 this.el.addClass('in fade');
24939 this.hoverState = null;
24941 if (this.el.hasClass('fade')) {
24952 //this.el.setXY([0,0]);
24953 this.el.removeClass('in');
24969 * @class Roo.bootstrap.LocationPicker
24970 * @extends Roo.bootstrap.Component
24971 * Bootstrap LocationPicker class
24972 * @cfg {Number} latitude Position when init default 0
24973 * @cfg {Number} longitude Position when init default 0
24974 * @cfg {Number} zoom default 15
24975 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24976 * @cfg {Boolean} mapTypeControl default false
24977 * @cfg {Boolean} disableDoubleClickZoom default false
24978 * @cfg {Boolean} scrollwheel default true
24979 * @cfg {Boolean} streetViewControl default false
24980 * @cfg {Number} radius default 0
24981 * @cfg {String} locationName
24982 * @cfg {Boolean} draggable default true
24983 * @cfg {Boolean} enableAutocomplete default false
24984 * @cfg {Boolean} enableReverseGeocode default true
24985 * @cfg {String} markerTitle
24988 * Create a new LocationPicker
24989 * @param {Object} config The config object
24993 Roo.bootstrap.LocationPicker = function(config){
24995 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25000 * Fires when the picker initialized.
25001 * @param {Roo.bootstrap.LocationPicker} this
25002 * @param {Google Location} location
25006 * @event positionchanged
25007 * Fires when the picker position changed.
25008 * @param {Roo.bootstrap.LocationPicker} this
25009 * @param {Google Location} location
25011 positionchanged : true,
25014 * Fires when the map resize.
25015 * @param {Roo.bootstrap.LocationPicker} this
25020 * Fires when the map show.
25021 * @param {Roo.bootstrap.LocationPicker} this
25026 * Fires when the map hide.
25027 * @param {Roo.bootstrap.LocationPicker} this
25032 * Fires when click the map.
25033 * @param {Roo.bootstrap.LocationPicker} this
25034 * @param {Map event} e
25038 * @event mapRightClick
25039 * Fires when right click the map.
25040 * @param {Roo.bootstrap.LocationPicker} this
25041 * @param {Map event} e
25043 mapRightClick : true,
25045 * @event markerClick
25046 * Fires when click the marker.
25047 * @param {Roo.bootstrap.LocationPicker} this
25048 * @param {Map event} e
25050 markerClick : true,
25052 * @event markerRightClick
25053 * Fires when right click the marker.
25054 * @param {Roo.bootstrap.LocationPicker} this
25055 * @param {Map event} e
25057 markerRightClick : true,
25059 * @event OverlayViewDraw
25060 * Fires when OverlayView Draw
25061 * @param {Roo.bootstrap.LocationPicker} this
25063 OverlayViewDraw : true,
25065 * @event OverlayViewOnAdd
25066 * Fires when OverlayView Draw
25067 * @param {Roo.bootstrap.LocationPicker} this
25069 OverlayViewOnAdd : true,
25071 * @event OverlayViewOnRemove
25072 * Fires when OverlayView Draw
25073 * @param {Roo.bootstrap.LocationPicker} this
25075 OverlayViewOnRemove : true,
25077 * @event OverlayViewShow
25078 * Fires when OverlayView Draw
25079 * @param {Roo.bootstrap.LocationPicker} this
25080 * @param {Pixel} cpx
25082 OverlayViewShow : true,
25084 * @event OverlayViewHide
25085 * Fires when OverlayView Draw
25086 * @param {Roo.bootstrap.LocationPicker} this
25088 OverlayViewHide : true,
25090 * @event loadexception
25091 * Fires when load google lib failed.
25092 * @param {Roo.bootstrap.LocationPicker} this
25094 loadexception : true
25099 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25101 gMapContext: false,
25107 mapTypeControl: false,
25108 disableDoubleClickZoom: false,
25110 streetViewControl: false,
25114 enableAutocomplete: false,
25115 enableReverseGeocode: true,
25118 getAutoCreate: function()
25123 cls: 'roo-location-picker'
25129 initEvents: function(ct, position)
25131 if(!this.el.getWidth() || this.isApplied()){
25135 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25140 initial: function()
25142 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25143 this.fireEvent('loadexception', this);
25147 if(!this.mapTypeId){
25148 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25151 this.gMapContext = this.GMapContext();
25153 this.initOverlayView();
25155 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25159 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25160 _this.setPosition(_this.gMapContext.marker.position);
25163 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25164 _this.fireEvent('mapClick', this, event);
25168 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25169 _this.fireEvent('mapRightClick', this, event);
25173 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25174 _this.fireEvent('markerClick', this, event);
25178 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25179 _this.fireEvent('markerRightClick', this, event);
25183 this.setPosition(this.gMapContext.location);
25185 this.fireEvent('initial', this, this.gMapContext.location);
25188 initOverlayView: function()
25192 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25196 _this.fireEvent('OverlayViewDraw', _this);
25201 _this.fireEvent('OverlayViewOnAdd', _this);
25204 onRemove: function()
25206 _this.fireEvent('OverlayViewOnRemove', _this);
25209 show: function(cpx)
25211 _this.fireEvent('OverlayViewShow', _this, cpx);
25216 _this.fireEvent('OverlayViewHide', _this);
25222 fromLatLngToContainerPixel: function(event)
25224 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25227 isApplied: function()
25229 return this.getGmapContext() == false ? false : true;
25232 getGmapContext: function()
25234 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25237 GMapContext: function()
25239 var position = new google.maps.LatLng(this.latitude, this.longitude);
25241 var _map = new google.maps.Map(this.el.dom, {
25244 mapTypeId: this.mapTypeId,
25245 mapTypeControl: this.mapTypeControl,
25246 disableDoubleClickZoom: this.disableDoubleClickZoom,
25247 scrollwheel: this.scrollwheel,
25248 streetViewControl: this.streetViewControl,
25249 locationName: this.locationName,
25250 draggable: this.draggable,
25251 enableAutocomplete: this.enableAutocomplete,
25252 enableReverseGeocode: this.enableReverseGeocode
25255 var _marker = new google.maps.Marker({
25256 position: position,
25258 title: this.markerTitle,
25259 draggable: this.draggable
25266 location: position,
25267 radius: this.radius,
25268 locationName: this.locationName,
25269 addressComponents: {
25270 formatted_address: null,
25271 addressLine1: null,
25272 addressLine2: null,
25274 streetNumber: null,
25278 stateOrProvince: null
25281 domContainer: this.el.dom,
25282 geodecoder: new google.maps.Geocoder()
25286 drawCircle: function(center, radius, options)
25288 if (this.gMapContext.circle != null) {
25289 this.gMapContext.circle.setMap(null);
25293 options = Roo.apply({}, options, {
25294 strokeColor: "#0000FF",
25295 strokeOpacity: .35,
25297 fillColor: "#0000FF",
25301 options.map = this.gMapContext.map;
25302 options.radius = radius;
25303 options.center = center;
25304 this.gMapContext.circle = new google.maps.Circle(options);
25305 return this.gMapContext.circle;
25311 setPosition: function(location)
25313 this.gMapContext.location = location;
25314 this.gMapContext.marker.setPosition(location);
25315 this.gMapContext.map.panTo(location);
25316 this.drawCircle(location, this.gMapContext.radius, {});
25320 if (this.gMapContext.settings.enableReverseGeocode) {
25321 this.gMapContext.geodecoder.geocode({
25322 latLng: this.gMapContext.location
25323 }, function(results, status) {
25325 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25326 _this.gMapContext.locationName = results[0].formatted_address;
25327 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25329 _this.fireEvent('positionchanged', this, location);
25336 this.fireEvent('positionchanged', this, location);
25341 google.maps.event.trigger(this.gMapContext.map, "resize");
25343 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25345 this.fireEvent('resize', this);
25348 setPositionByLatLng: function(latitude, longitude)
25350 this.setPosition(new google.maps.LatLng(latitude, longitude));
25353 getCurrentPosition: function()
25356 latitude: this.gMapContext.location.lat(),
25357 longitude: this.gMapContext.location.lng()
25361 getAddressName: function()
25363 return this.gMapContext.locationName;
25366 getAddressComponents: function()
25368 return this.gMapContext.addressComponents;
25371 address_component_from_google_geocode: function(address_components)
25375 for (var i = 0; i < address_components.length; i++) {
25376 var component = address_components[i];
25377 if (component.types.indexOf("postal_code") >= 0) {
25378 result.postalCode = component.short_name;
25379 } else if (component.types.indexOf("street_number") >= 0) {
25380 result.streetNumber = component.short_name;
25381 } else if (component.types.indexOf("route") >= 0) {
25382 result.streetName = component.short_name;
25383 } else if (component.types.indexOf("neighborhood") >= 0) {
25384 result.city = component.short_name;
25385 } else if (component.types.indexOf("locality") >= 0) {
25386 result.city = component.short_name;
25387 } else if (component.types.indexOf("sublocality") >= 0) {
25388 result.district = component.short_name;
25389 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25390 result.stateOrProvince = component.short_name;
25391 } else if (component.types.indexOf("country") >= 0) {
25392 result.country = component.short_name;
25396 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25397 result.addressLine2 = "";
25401 setZoomLevel: function(zoom)
25403 this.gMapContext.map.setZoom(zoom);
25416 this.fireEvent('show', this);
25427 this.fireEvent('hide', this);
25432 Roo.apply(Roo.bootstrap.LocationPicker, {
25434 OverlayView : function(map, options)
25436 options = options || {};
25450 * @class Roo.bootstrap.Alert
25451 * @extends Roo.bootstrap.Component
25452 * Bootstrap Alert class
25453 * @cfg {String} title The title of alert
25454 * @cfg {String} html The content of alert
25455 * @cfg {String} weight ( success | info | warning | danger )
25456 * @cfg {String} faicon font-awesomeicon
25459 * Create a new alert
25460 * @param {Object} config The config object
25464 Roo.bootstrap.Alert = function(config){
25465 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25469 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25476 getAutoCreate : function()
25485 cls : 'roo-alert-icon'
25490 cls : 'roo-alert-title',
25495 cls : 'roo-alert-text',
25502 cfg.cn[0].cls += ' fa ' + this.faicon;
25506 cfg.cls += ' alert-' + this.weight;
25512 initEvents: function()
25514 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25517 setTitle : function(str)
25519 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25522 setText : function(str)
25524 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25527 setWeight : function(weight)
25530 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25533 this.weight = weight;
25535 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25538 setIcon : function(icon)
25541 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25544 this.faicon = icon;
25546 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25567 * @class Roo.bootstrap.UploadCropbox
25568 * @extends Roo.bootstrap.Component
25569 * Bootstrap UploadCropbox class
25570 * @cfg {String} emptyText show when image has been loaded
25571 * @cfg {String} rotateNotify show when image too small to rotate
25572 * @cfg {Number} errorTimeout default 3000
25573 * @cfg {Number} minWidth default 300
25574 * @cfg {Number} minHeight default 300
25575 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25576 * @cfg {Boolean} isDocument (true|false) default false
25577 * @cfg {String} url action url
25578 * @cfg {String} paramName default 'imageUpload'
25579 * @cfg {String} method default POST
25580 * @cfg {Boolean} loadMask (true|false) default true
25581 * @cfg {Boolean} loadingText default 'Loading...'
25584 * Create a new UploadCropbox
25585 * @param {Object} config The config object
25588 Roo.bootstrap.UploadCropbox = function(config){
25589 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25593 * @event beforeselectfile
25594 * Fire before select file
25595 * @param {Roo.bootstrap.UploadCropbox} this
25597 "beforeselectfile" : true,
25600 * Fire after initEvent
25601 * @param {Roo.bootstrap.UploadCropbox} this
25606 * Fire after initEvent
25607 * @param {Roo.bootstrap.UploadCropbox} this
25608 * @param {String} data
25613 * Fire when preparing the file data
25614 * @param {Roo.bootstrap.UploadCropbox} this
25615 * @param {Object} file
25620 * Fire when get exception
25621 * @param {Roo.bootstrap.UploadCropbox} this
25622 * @param {XMLHttpRequest} xhr
25624 "exception" : true,
25626 * @event beforeloadcanvas
25627 * Fire before load the canvas
25628 * @param {Roo.bootstrap.UploadCropbox} this
25629 * @param {String} src
25631 "beforeloadcanvas" : true,
25634 * Fire when trash image
25635 * @param {Roo.bootstrap.UploadCropbox} this
25640 * Fire when download the image
25641 * @param {Roo.bootstrap.UploadCropbox} this
25645 * @event footerbuttonclick
25646 * Fire when footerbuttonclick
25647 * @param {Roo.bootstrap.UploadCropbox} this
25648 * @param {String} type
25650 "footerbuttonclick" : true,
25654 * @param {Roo.bootstrap.UploadCropbox} this
25659 * Fire when rotate the image
25660 * @param {Roo.bootstrap.UploadCropbox} this
25661 * @param {String} pos
25666 * Fire when inspect the file
25667 * @param {Roo.bootstrap.UploadCropbox} this
25668 * @param {Object} file
25673 * Fire when xhr upload the file
25674 * @param {Roo.bootstrap.UploadCropbox} this
25675 * @param {Object} data
25680 * Fire when arrange the file data
25681 * @param {Roo.bootstrap.UploadCropbox} this
25682 * @param {Object} formData
25687 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25690 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25692 emptyText : 'Click to upload image',
25693 rotateNotify : 'Image is too small to rotate',
25694 errorTimeout : 3000,
25708 cropType : 'image/jpeg',
25710 canvasLoaded : false,
25711 isDocument : false,
25713 paramName : 'imageUpload',
25715 loadingText : 'Loading...',
25718 getAutoCreate : function()
25722 cls : 'roo-upload-cropbox',
25726 cls : 'roo-upload-cropbox-selector',
25731 cls : 'roo-upload-cropbox-body',
25732 style : 'cursor:pointer',
25736 cls : 'roo-upload-cropbox-preview'
25740 cls : 'roo-upload-cropbox-thumb'
25744 cls : 'roo-upload-cropbox-empty-notify',
25745 html : this.emptyText
25749 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25750 html : this.rotateNotify
25756 cls : 'roo-upload-cropbox-footer',
25759 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25769 onRender : function(ct, position)
25771 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25773 if (this.buttons.length) {
25775 Roo.each(this.buttons, function(bb) {
25777 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25779 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25785 this.maskEl = this.el;
25789 initEvents : function()
25791 this.urlAPI = (window.createObjectURL && window) ||
25792 (window.URL && URL.revokeObjectURL && URL) ||
25793 (window.webkitURL && webkitURL);
25795 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25796 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25798 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25799 this.selectorEl.hide();
25801 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25802 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25804 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25805 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25806 this.thumbEl.hide();
25808 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25809 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25811 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25812 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25813 this.errorEl.hide();
25815 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25816 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25817 this.footerEl.hide();
25819 this.setThumbBoxSize();
25825 this.fireEvent('initial', this);
25832 window.addEventListener("resize", function() { _this.resize(); } );
25834 this.bodyEl.on('click', this.beforeSelectFile, this);
25837 this.bodyEl.on('touchstart', this.onTouchStart, this);
25838 this.bodyEl.on('touchmove', this.onTouchMove, this);
25839 this.bodyEl.on('touchend', this.onTouchEnd, this);
25843 this.bodyEl.on('mousedown', this.onMouseDown, this);
25844 this.bodyEl.on('mousemove', this.onMouseMove, this);
25845 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25846 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25847 Roo.get(document).on('mouseup', this.onMouseUp, this);
25850 this.selectorEl.on('change', this.onFileSelected, this);
25856 this.baseScale = 1;
25858 this.baseRotate = 1;
25859 this.dragable = false;
25860 this.pinching = false;
25863 this.cropData = false;
25864 this.notifyEl.dom.innerHTML = this.emptyText;
25866 this.selectorEl.dom.value = '';
25870 resize : function()
25872 if(this.fireEvent('resize', this) != false){
25873 this.setThumbBoxPosition();
25874 this.setCanvasPosition();
25878 onFooterButtonClick : function(e, el, o, type)
25881 case 'rotate-left' :
25882 this.onRotateLeft(e);
25884 case 'rotate-right' :
25885 this.onRotateRight(e);
25888 this.beforeSelectFile(e);
25903 this.fireEvent('footerbuttonclick', this, type);
25906 beforeSelectFile : function(e)
25908 e.preventDefault();
25910 if(this.fireEvent('beforeselectfile', this) != false){
25911 this.selectorEl.dom.click();
25915 onFileSelected : function(e)
25917 e.preventDefault();
25919 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25923 var file = this.selectorEl.dom.files[0];
25925 if(this.fireEvent('inspect', this, file) != false){
25926 this.prepare(file);
25931 trash : function(e)
25933 this.fireEvent('trash', this);
25936 download : function(e)
25938 this.fireEvent('download', this);
25941 loadCanvas : function(src)
25943 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25947 this.imageEl = document.createElement('img');
25951 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25953 this.imageEl.src = src;
25957 onLoadCanvas : function()
25959 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25960 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25962 this.bodyEl.un('click', this.beforeSelectFile, this);
25964 this.notifyEl.hide();
25965 this.thumbEl.show();
25966 this.footerEl.show();
25968 this.baseRotateLevel();
25970 if(this.isDocument){
25971 this.setThumbBoxSize();
25974 this.setThumbBoxPosition();
25976 this.baseScaleLevel();
25982 this.canvasLoaded = true;
25985 this.maskEl.unmask();
25990 setCanvasPosition : function()
25992 if(!this.canvasEl){
25996 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25997 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25999 this.previewEl.setLeft(pw);
26000 this.previewEl.setTop(ph);
26004 onMouseDown : function(e)
26008 this.dragable = true;
26009 this.pinching = false;
26011 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26012 this.dragable = false;
26016 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26017 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26021 onMouseMove : function(e)
26025 if(!this.canvasLoaded){
26029 if (!this.dragable){
26033 var minX = Math.ceil(this.thumbEl.getLeft(true));
26034 var minY = Math.ceil(this.thumbEl.getTop(true));
26036 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26037 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26039 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26040 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26042 x = x - this.mouseX;
26043 y = y - this.mouseY;
26045 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26046 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26048 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26049 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26051 this.previewEl.setLeft(bgX);
26052 this.previewEl.setTop(bgY);
26054 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26055 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26058 onMouseUp : function(e)
26062 this.dragable = false;
26065 onMouseWheel : function(e)
26069 this.startScale = this.scale;
26071 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26073 if(!this.zoomable()){
26074 this.scale = this.startScale;
26083 zoomable : function()
26085 var minScale = this.thumbEl.getWidth() / this.minWidth;
26087 if(this.minWidth < this.minHeight){
26088 minScale = this.thumbEl.getHeight() / this.minHeight;
26091 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26092 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26096 (this.rotate == 0 || this.rotate == 180) &&
26098 width > this.imageEl.OriginWidth ||
26099 height > this.imageEl.OriginHeight ||
26100 (width < this.minWidth && height < this.minHeight)
26108 (this.rotate == 90 || this.rotate == 270) &&
26110 width > this.imageEl.OriginWidth ||
26111 height > this.imageEl.OriginHeight ||
26112 (width < this.minHeight && height < this.minWidth)
26119 !this.isDocument &&
26120 (this.rotate == 0 || this.rotate == 180) &&
26122 width < this.minWidth ||
26123 width > this.imageEl.OriginWidth ||
26124 height < this.minHeight ||
26125 height > this.imageEl.OriginHeight
26132 !this.isDocument &&
26133 (this.rotate == 90 || this.rotate == 270) &&
26135 width < this.minHeight ||
26136 width > this.imageEl.OriginWidth ||
26137 height < this.minWidth ||
26138 height > this.imageEl.OriginHeight
26148 onRotateLeft : function(e)
26150 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26152 var minScale = this.thumbEl.getWidth() / this.minWidth;
26154 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26155 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26157 this.startScale = this.scale;
26159 while (this.getScaleLevel() < minScale){
26161 this.scale = this.scale + 1;
26163 if(!this.zoomable()){
26168 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26169 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26174 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26181 this.scale = this.startScale;
26183 this.onRotateFail();
26188 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26190 if(this.isDocument){
26191 this.setThumbBoxSize();
26192 this.setThumbBoxPosition();
26193 this.setCanvasPosition();
26198 this.fireEvent('rotate', this, 'left');
26202 onRotateRight : function(e)
26204 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26206 var minScale = this.thumbEl.getWidth() / this.minWidth;
26208 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26209 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26211 this.startScale = this.scale;
26213 while (this.getScaleLevel() < minScale){
26215 this.scale = this.scale + 1;
26217 if(!this.zoomable()){
26222 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26223 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26228 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26235 this.scale = this.startScale;
26237 this.onRotateFail();
26242 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26244 if(this.isDocument){
26245 this.setThumbBoxSize();
26246 this.setThumbBoxPosition();
26247 this.setCanvasPosition();
26252 this.fireEvent('rotate', this, 'right');
26255 onRotateFail : function()
26257 this.errorEl.show(true);
26261 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26266 this.previewEl.dom.innerHTML = '';
26268 var canvasEl = document.createElement("canvas");
26270 var contextEl = canvasEl.getContext("2d");
26272 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26273 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26274 var center = this.imageEl.OriginWidth / 2;
26276 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26277 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26278 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26279 center = this.imageEl.OriginHeight / 2;
26282 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26284 contextEl.translate(center, center);
26285 contextEl.rotate(this.rotate * Math.PI / 180);
26287 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26289 this.canvasEl = document.createElement("canvas");
26291 this.contextEl = this.canvasEl.getContext("2d");
26293 switch (this.rotate) {
26296 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26297 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26299 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26304 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26305 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26307 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26308 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);
26312 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26317 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26318 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26320 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26321 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);
26325 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);
26330 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26331 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26333 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26334 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26338 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);
26345 this.previewEl.appendChild(this.canvasEl);
26347 this.setCanvasPosition();
26352 if(!this.canvasLoaded){
26356 var imageCanvas = document.createElement("canvas");
26358 var imageContext = imageCanvas.getContext("2d");
26360 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26361 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26363 var center = imageCanvas.width / 2;
26365 imageContext.translate(center, center);
26367 imageContext.rotate(this.rotate * Math.PI / 180);
26369 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26371 var canvas = document.createElement("canvas");
26373 var context = canvas.getContext("2d");
26375 canvas.width = this.minWidth;
26376 canvas.height = this.minHeight;
26378 switch (this.rotate) {
26381 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26382 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26384 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26385 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26387 var targetWidth = this.minWidth - 2 * x;
26388 var targetHeight = this.minHeight - 2 * y;
26392 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26393 scale = targetWidth / width;
26396 if(x > 0 && y == 0){
26397 scale = targetHeight / height;
26400 if(x > 0 && y > 0){
26401 scale = targetWidth / width;
26403 if(width < height){
26404 scale = targetHeight / height;
26408 context.scale(scale, scale);
26410 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26411 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26413 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26414 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26416 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26421 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26422 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26424 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26425 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26427 var targetWidth = this.minWidth - 2 * x;
26428 var targetHeight = this.minHeight - 2 * y;
26432 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26433 scale = targetWidth / width;
26436 if(x > 0 && y == 0){
26437 scale = targetHeight / height;
26440 if(x > 0 && y > 0){
26441 scale = targetWidth / width;
26443 if(width < height){
26444 scale = targetHeight / height;
26448 context.scale(scale, scale);
26450 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26451 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26453 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26454 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26456 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26458 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26463 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26464 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26466 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26467 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26469 var targetWidth = this.minWidth - 2 * x;
26470 var targetHeight = this.minHeight - 2 * y;
26474 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26475 scale = targetWidth / width;
26478 if(x > 0 && y == 0){
26479 scale = targetHeight / height;
26482 if(x > 0 && y > 0){
26483 scale = targetWidth / width;
26485 if(width < height){
26486 scale = targetHeight / height;
26490 context.scale(scale, scale);
26492 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26493 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26495 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26496 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26498 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26499 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26501 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26506 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26507 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26509 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26510 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26512 var targetWidth = this.minWidth - 2 * x;
26513 var targetHeight = this.minHeight - 2 * y;
26517 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26518 scale = targetWidth / width;
26521 if(x > 0 && y == 0){
26522 scale = targetHeight / height;
26525 if(x > 0 && y > 0){
26526 scale = targetWidth / width;
26528 if(width < height){
26529 scale = targetHeight / height;
26533 context.scale(scale, scale);
26535 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26536 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26538 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26539 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26541 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26543 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26550 this.cropData = canvas.toDataURL(this.cropType);
26552 if(this.fireEvent('crop', this, this.cropData) !== false){
26553 this.process(this.file, this.cropData);
26560 setThumbBoxSize : function()
26564 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26565 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26566 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26568 this.minWidth = width;
26569 this.minHeight = height;
26571 if(this.rotate == 90 || this.rotate == 270){
26572 this.minWidth = height;
26573 this.minHeight = width;
26578 width = Math.ceil(this.minWidth * height / this.minHeight);
26580 if(this.minWidth > this.minHeight){
26582 height = Math.ceil(this.minHeight * width / this.minWidth);
26585 this.thumbEl.setStyle({
26586 width : width + 'px',
26587 height : height + 'px'
26594 setThumbBoxPosition : function()
26596 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26597 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26599 this.thumbEl.setLeft(x);
26600 this.thumbEl.setTop(y);
26604 baseRotateLevel : function()
26606 this.baseRotate = 1;
26609 typeof(this.exif) != 'undefined' &&
26610 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26611 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26613 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26616 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26620 baseScaleLevel : function()
26624 if(this.isDocument){
26626 if(this.baseRotate == 6 || this.baseRotate == 8){
26628 height = this.thumbEl.getHeight();
26629 this.baseScale = height / this.imageEl.OriginWidth;
26631 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26632 width = this.thumbEl.getWidth();
26633 this.baseScale = width / this.imageEl.OriginHeight;
26639 height = this.thumbEl.getHeight();
26640 this.baseScale = height / this.imageEl.OriginHeight;
26642 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26643 width = this.thumbEl.getWidth();
26644 this.baseScale = width / this.imageEl.OriginWidth;
26650 if(this.baseRotate == 6 || this.baseRotate == 8){
26652 width = this.thumbEl.getHeight();
26653 this.baseScale = width / this.imageEl.OriginHeight;
26655 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26656 height = this.thumbEl.getWidth();
26657 this.baseScale = height / this.imageEl.OriginHeight;
26660 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26661 height = this.thumbEl.getWidth();
26662 this.baseScale = height / this.imageEl.OriginHeight;
26664 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26665 width = this.thumbEl.getHeight();
26666 this.baseScale = width / this.imageEl.OriginWidth;
26673 width = this.thumbEl.getWidth();
26674 this.baseScale = width / this.imageEl.OriginWidth;
26676 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26677 height = this.thumbEl.getHeight();
26678 this.baseScale = height / this.imageEl.OriginHeight;
26681 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26683 height = this.thumbEl.getHeight();
26684 this.baseScale = height / this.imageEl.OriginHeight;
26686 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26687 width = this.thumbEl.getWidth();
26688 this.baseScale = width / this.imageEl.OriginWidth;
26696 getScaleLevel : function()
26698 return this.baseScale * Math.pow(1.1, this.scale);
26701 onTouchStart : function(e)
26703 if(!this.canvasLoaded){
26704 this.beforeSelectFile(e);
26708 var touches = e.browserEvent.touches;
26714 if(touches.length == 1){
26715 this.onMouseDown(e);
26719 if(touches.length != 2){
26725 for(var i = 0, finger; finger = touches[i]; i++){
26726 coords.push(finger.pageX, finger.pageY);
26729 var x = Math.pow(coords[0] - coords[2], 2);
26730 var y = Math.pow(coords[1] - coords[3], 2);
26732 this.startDistance = Math.sqrt(x + y);
26734 this.startScale = this.scale;
26736 this.pinching = true;
26737 this.dragable = false;
26741 onTouchMove : function(e)
26743 if(!this.pinching && !this.dragable){
26747 var touches = e.browserEvent.touches;
26754 this.onMouseMove(e);
26760 for(var i = 0, finger; finger = touches[i]; i++){
26761 coords.push(finger.pageX, finger.pageY);
26764 var x = Math.pow(coords[0] - coords[2], 2);
26765 var y = Math.pow(coords[1] - coords[3], 2);
26767 this.endDistance = Math.sqrt(x + y);
26769 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26771 if(!this.zoomable()){
26772 this.scale = this.startScale;
26780 onTouchEnd : function(e)
26782 this.pinching = false;
26783 this.dragable = false;
26787 process : function(file, crop)
26790 this.maskEl.mask(this.loadingText);
26793 this.xhr = new XMLHttpRequest();
26795 file.xhr = this.xhr;
26797 this.xhr.open(this.method, this.url, true);
26800 "Accept": "application/json",
26801 "Cache-Control": "no-cache",
26802 "X-Requested-With": "XMLHttpRequest"
26805 for (var headerName in headers) {
26806 var headerValue = headers[headerName];
26808 this.xhr.setRequestHeader(headerName, headerValue);
26814 this.xhr.onload = function()
26816 _this.xhrOnLoad(_this.xhr);
26819 this.xhr.onerror = function()
26821 _this.xhrOnError(_this.xhr);
26824 var formData = new FormData();
26826 formData.append('returnHTML', 'NO');
26829 formData.append('crop', crop);
26832 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26833 formData.append(this.paramName, file, file.name);
26836 if(typeof(file.filename) != 'undefined'){
26837 formData.append('filename', file.filename);
26840 if(typeof(file.mimetype) != 'undefined'){
26841 formData.append('mimetype', file.mimetype);
26844 if(this.fireEvent('arrange', this, formData) != false){
26845 this.xhr.send(formData);
26849 xhrOnLoad : function(xhr)
26852 this.maskEl.unmask();
26855 if (xhr.readyState !== 4) {
26856 this.fireEvent('exception', this, xhr);
26860 var response = Roo.decode(xhr.responseText);
26862 if(!response.success){
26863 this.fireEvent('exception', this, xhr);
26867 var response = Roo.decode(xhr.responseText);
26869 this.fireEvent('upload', this, response);
26873 xhrOnError : function()
26876 this.maskEl.unmask();
26879 Roo.log('xhr on error');
26881 var response = Roo.decode(xhr.responseText);
26887 prepare : function(file)
26890 this.maskEl.mask(this.loadingText);
26896 if(typeof(file) === 'string'){
26897 this.loadCanvas(file);
26901 if(!file || !this.urlAPI){
26906 this.cropType = file.type;
26910 if(this.fireEvent('prepare', this, this.file) != false){
26912 var reader = new FileReader();
26914 reader.onload = function (e) {
26915 if (e.target.error) {
26916 Roo.log(e.target.error);
26920 var buffer = e.target.result,
26921 dataView = new DataView(buffer),
26923 maxOffset = dataView.byteLength - 4,
26927 if (dataView.getUint16(0) === 0xffd8) {
26928 while (offset < maxOffset) {
26929 markerBytes = dataView.getUint16(offset);
26931 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26932 markerLength = dataView.getUint16(offset + 2) + 2;
26933 if (offset + markerLength > dataView.byteLength) {
26934 Roo.log('Invalid meta data: Invalid segment size.');
26938 if(markerBytes == 0xffe1){
26939 _this.parseExifData(
26946 offset += markerLength;
26956 var url = _this.urlAPI.createObjectURL(_this.file);
26958 _this.loadCanvas(url);
26963 reader.readAsArrayBuffer(this.file);
26969 parseExifData : function(dataView, offset, length)
26971 var tiffOffset = offset + 10,
26975 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26976 // No Exif data, might be XMP data instead
26980 // Check for the ASCII code for "Exif" (0x45786966):
26981 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26982 // No Exif data, might be XMP data instead
26985 if (tiffOffset + 8 > dataView.byteLength) {
26986 Roo.log('Invalid Exif data: Invalid segment size.');
26989 // Check for the two null bytes:
26990 if (dataView.getUint16(offset + 8) !== 0x0000) {
26991 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26994 // Check the byte alignment:
26995 switch (dataView.getUint16(tiffOffset)) {
26997 littleEndian = true;
27000 littleEndian = false;
27003 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27006 // Check for the TIFF tag marker (0x002A):
27007 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27008 Roo.log('Invalid Exif data: Missing TIFF marker.');
27011 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27012 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27014 this.parseExifTags(
27017 tiffOffset + dirOffset,
27022 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27027 if (dirOffset + 6 > dataView.byteLength) {
27028 Roo.log('Invalid Exif data: Invalid directory offset.');
27031 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27032 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27033 if (dirEndOffset + 4 > dataView.byteLength) {
27034 Roo.log('Invalid Exif data: Invalid directory size.');
27037 for (i = 0; i < tagsNumber; i += 1) {
27041 dirOffset + 2 + 12 * i, // tag offset
27045 // Return the offset to the next directory:
27046 return dataView.getUint32(dirEndOffset, littleEndian);
27049 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27051 var tag = dataView.getUint16(offset, littleEndian);
27053 this.exif[tag] = this.getExifValue(
27057 dataView.getUint16(offset + 2, littleEndian), // tag type
27058 dataView.getUint32(offset + 4, littleEndian), // tag length
27063 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27065 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27074 Roo.log('Invalid Exif data: Invalid tag type.');
27078 tagSize = tagType.size * length;
27079 // Determine if the value is contained in the dataOffset bytes,
27080 // or if the value at the dataOffset is a pointer to the actual data:
27081 dataOffset = tagSize > 4 ?
27082 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27083 if (dataOffset + tagSize > dataView.byteLength) {
27084 Roo.log('Invalid Exif data: Invalid data offset.');
27087 if (length === 1) {
27088 return tagType.getValue(dataView, dataOffset, littleEndian);
27091 for (i = 0; i < length; i += 1) {
27092 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27095 if (tagType.ascii) {
27097 // Concatenate the chars:
27098 for (i = 0; i < values.length; i += 1) {
27100 // Ignore the terminating NULL byte(s):
27101 if (c === '\u0000') {
27113 Roo.apply(Roo.bootstrap.UploadCropbox, {
27115 'Orientation': 0x0112
27119 1: 0, //'top-left',
27121 3: 180, //'bottom-right',
27122 // 4: 'bottom-left',
27124 6: 90, //'right-top',
27125 // 7: 'right-bottom',
27126 8: 270 //'left-bottom'
27130 // byte, 8-bit unsigned int:
27132 getValue: function (dataView, dataOffset) {
27133 return dataView.getUint8(dataOffset);
27137 // ascii, 8-bit byte:
27139 getValue: function (dataView, dataOffset) {
27140 return String.fromCharCode(dataView.getUint8(dataOffset));
27145 // short, 16 bit int:
27147 getValue: function (dataView, dataOffset, littleEndian) {
27148 return dataView.getUint16(dataOffset, littleEndian);
27152 // long, 32 bit int:
27154 getValue: function (dataView, dataOffset, littleEndian) {
27155 return dataView.getUint32(dataOffset, littleEndian);
27159 // rational = two long values, first is numerator, second is denominator:
27161 getValue: function (dataView, dataOffset, littleEndian) {
27162 return dataView.getUint32(dataOffset, littleEndian) /
27163 dataView.getUint32(dataOffset + 4, littleEndian);
27167 // slong, 32 bit signed int:
27169 getValue: function (dataView, dataOffset, littleEndian) {
27170 return dataView.getInt32(dataOffset, littleEndian);
27174 // srational, two slongs, first is numerator, second is denominator:
27176 getValue: function (dataView, dataOffset, littleEndian) {
27177 return dataView.getInt32(dataOffset, littleEndian) /
27178 dataView.getInt32(dataOffset + 4, littleEndian);
27188 cls : 'btn-group roo-upload-cropbox-rotate-left',
27189 action : 'rotate-left',
27193 cls : 'btn btn-default',
27194 html : '<i class="fa fa-undo"></i>'
27200 cls : 'btn-group roo-upload-cropbox-picture',
27201 action : 'picture',
27205 cls : 'btn btn-default',
27206 html : '<i class="fa fa-picture-o"></i>'
27212 cls : 'btn-group roo-upload-cropbox-rotate-right',
27213 action : 'rotate-right',
27217 cls : 'btn btn-default',
27218 html : '<i class="fa fa-repeat"></i>'
27226 cls : 'btn-group roo-upload-cropbox-rotate-left',
27227 action : 'rotate-left',
27231 cls : 'btn btn-default',
27232 html : '<i class="fa fa-undo"></i>'
27238 cls : 'btn-group roo-upload-cropbox-download',
27239 action : 'download',
27243 cls : 'btn btn-default',
27244 html : '<i class="fa fa-download"></i>'
27250 cls : 'btn-group roo-upload-cropbox-crop',
27255 cls : 'btn btn-default',
27256 html : '<i class="fa fa-crop"></i>'
27262 cls : 'btn-group roo-upload-cropbox-trash',
27267 cls : 'btn btn-default',
27268 html : '<i class="fa fa-trash"></i>'
27274 cls : 'btn-group roo-upload-cropbox-rotate-right',
27275 action : 'rotate-right',
27279 cls : 'btn btn-default',
27280 html : '<i class="fa fa-repeat"></i>'
27288 cls : 'btn-group roo-upload-cropbox-rotate-left',
27289 action : 'rotate-left',
27293 cls : 'btn btn-default',
27294 html : '<i class="fa fa-undo"></i>'
27300 cls : 'btn-group roo-upload-cropbox-rotate-right',
27301 action : 'rotate-right',
27305 cls : 'btn btn-default',
27306 html : '<i class="fa fa-repeat"></i>'
27319 * @class Roo.bootstrap.DocumentManager
27320 * @extends Roo.bootstrap.Component
27321 * Bootstrap DocumentManager class
27322 * @cfg {String} paramName default 'imageUpload'
27323 * @cfg {String} toolTipName default 'filename'
27324 * @cfg {String} method default POST
27325 * @cfg {String} url action url
27326 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27327 * @cfg {Boolean} multiple multiple upload default true
27328 * @cfg {Number} thumbSize default 300
27329 * @cfg {String} fieldLabel
27330 * @cfg {Number} labelWidth default 4
27331 * @cfg {String} labelAlign (left|top) default left
27332 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27335 * Create a new DocumentManager
27336 * @param {Object} config The config object
27339 Roo.bootstrap.DocumentManager = function(config){
27340 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27343 this.delegates = [];
27348 * Fire when initial the DocumentManager
27349 * @param {Roo.bootstrap.DocumentManager} this
27354 * inspect selected file
27355 * @param {Roo.bootstrap.DocumentManager} this
27356 * @param {File} file
27361 * Fire when xhr load exception
27362 * @param {Roo.bootstrap.DocumentManager} this
27363 * @param {XMLHttpRequest} xhr
27365 "exception" : true,
27367 * @event afterupload
27368 * Fire when xhr load exception
27369 * @param {Roo.bootstrap.DocumentManager} this
27370 * @param {XMLHttpRequest} xhr
27372 "afterupload" : true,
27375 * prepare the form data
27376 * @param {Roo.bootstrap.DocumentManager} this
27377 * @param {Object} formData
27382 * Fire when remove the file
27383 * @param {Roo.bootstrap.DocumentManager} this
27384 * @param {Object} file
27389 * Fire after refresh the file
27390 * @param {Roo.bootstrap.DocumentManager} this
27395 * Fire after click the image
27396 * @param {Roo.bootstrap.DocumentManager} this
27397 * @param {Object} file
27402 * Fire when upload a image and editable set to true
27403 * @param {Roo.bootstrap.DocumentManager} this
27404 * @param {Object} file
27408 * @event beforeselectfile
27409 * Fire before select file
27410 * @param {Roo.bootstrap.DocumentManager} this
27412 "beforeselectfile" : true,
27415 * Fire before process file
27416 * @param {Roo.bootstrap.DocumentManager} this
27417 * @param {Object} file
27424 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27433 paramName : 'imageUpload',
27434 toolTipName : 'filename',
27437 labelAlign : 'left',
27442 getAutoCreate : function()
27444 var managerWidget = {
27446 cls : 'roo-document-manager',
27450 cls : 'roo-document-manager-selector',
27455 cls : 'roo-document-manager-uploader',
27459 cls : 'roo-document-manager-upload-btn',
27460 html : '<i class="fa fa-plus"></i>'
27471 cls : 'column col-md-12',
27476 if(this.fieldLabel.length){
27481 cls : 'column col-md-12',
27482 html : this.fieldLabel
27486 cls : 'column col-md-12',
27491 if(this.labelAlign == 'left'){
27495 cls : 'column col-md-' + this.labelWidth,
27496 html : this.fieldLabel
27500 cls : 'column col-md-' + (12 - this.labelWidth),
27510 cls : 'row clearfix',
27518 initEvents : function()
27520 this.managerEl = this.el.select('.roo-document-manager', true).first();
27521 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27523 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27524 this.selectorEl.hide();
27527 this.selectorEl.attr('multiple', 'multiple');
27530 this.selectorEl.on('change', this.onFileSelected, this);
27532 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27533 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27535 this.uploader.on('click', this.onUploaderClick, this);
27537 this.renderProgressDialog();
27541 window.addEventListener("resize", function() { _this.refresh(); } );
27543 this.fireEvent('initial', this);
27546 renderProgressDialog : function()
27550 this.progressDialog = new Roo.bootstrap.Modal({
27551 cls : 'roo-document-manager-progress-dialog',
27552 allow_close : false,
27562 btnclick : function() {
27563 _this.uploadCancel();
27569 this.progressDialog.render(Roo.get(document.body));
27571 this.progress = new Roo.bootstrap.Progress({
27572 cls : 'roo-document-manager-progress',
27577 this.progress.render(this.progressDialog.getChildContainer());
27579 this.progressBar = new Roo.bootstrap.ProgressBar({
27580 cls : 'roo-document-manager-progress-bar',
27583 aria_valuemax : 12,
27587 this.progressBar.render(this.progress.getChildContainer());
27590 onUploaderClick : function(e)
27592 e.preventDefault();
27594 if(this.fireEvent('beforeselectfile', this) != false){
27595 this.selectorEl.dom.click();
27600 onFileSelected : function(e)
27602 e.preventDefault();
27604 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27608 Roo.each(this.selectorEl.dom.files, function(file){
27609 if(this.fireEvent('inspect', this, file) != false){
27610 this.files.push(file);
27620 this.selectorEl.dom.value = '';
27622 if(!this.files.length){
27626 if(this.boxes > 0 && this.files.length > this.boxes){
27627 this.files = this.files.slice(0, this.boxes);
27630 this.uploader.show();
27632 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27633 this.uploader.hide();
27642 Roo.each(this.files, function(file){
27644 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27645 var f = this.renderPreview(file);
27650 if(file.type.indexOf('image') != -1){
27651 this.delegates.push(
27653 _this.process(file);
27654 }).createDelegate(this)
27662 _this.process(file);
27663 }).createDelegate(this)
27668 this.files = files;
27670 this.delegates = this.delegates.concat(docs);
27672 if(!this.delegates.length){
27677 this.progressBar.aria_valuemax = this.delegates.length;
27684 arrange : function()
27686 if(!this.delegates.length){
27687 this.progressDialog.hide();
27692 var delegate = this.delegates.shift();
27694 this.progressDialog.show();
27696 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27698 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27703 refresh : function()
27705 this.uploader.show();
27707 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27708 this.uploader.hide();
27711 Roo.isTouch ? this.closable(false) : this.closable(true);
27713 this.fireEvent('refresh', this);
27716 onRemove : function(e, el, o)
27718 e.preventDefault();
27720 this.fireEvent('remove', this, o);
27724 remove : function(o)
27728 Roo.each(this.files, function(file){
27729 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27738 this.files = files;
27745 Roo.each(this.files, function(file){
27750 file.target.remove();
27759 onClick : function(e, el, o)
27761 e.preventDefault();
27763 this.fireEvent('click', this, o);
27767 closable : function(closable)
27769 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27771 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27783 xhrOnLoad : function(xhr)
27785 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27789 if (xhr.readyState !== 4) {
27791 this.fireEvent('exception', this, xhr);
27795 var response = Roo.decode(xhr.responseText);
27797 if(!response.success){
27799 this.fireEvent('exception', this, xhr);
27803 var file = this.renderPreview(response.data);
27805 this.files.push(file);
27809 this.fireEvent('afterupload', this, xhr);
27813 xhrOnError : function(xhr)
27815 Roo.log('xhr on error');
27817 var response = Roo.decode(xhr.responseText);
27824 process : function(file)
27826 if(this.fireEvent('process', this, file) !== false){
27827 if(this.editable && file.type.indexOf('image') != -1){
27828 this.fireEvent('edit', this, file);
27832 this.uploadStart(file, false);
27839 uploadStart : function(file, crop)
27841 this.xhr = new XMLHttpRequest();
27843 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27848 file.xhr = this.xhr;
27850 this.managerEl.createChild({
27852 cls : 'roo-document-manager-loading',
27856 tooltip : file.name,
27857 cls : 'roo-document-manager-thumb',
27858 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27864 this.xhr.open(this.method, this.url, true);
27867 "Accept": "application/json",
27868 "Cache-Control": "no-cache",
27869 "X-Requested-With": "XMLHttpRequest"
27872 for (var headerName in headers) {
27873 var headerValue = headers[headerName];
27875 this.xhr.setRequestHeader(headerName, headerValue);
27881 this.xhr.onload = function()
27883 _this.xhrOnLoad(_this.xhr);
27886 this.xhr.onerror = function()
27888 _this.xhrOnError(_this.xhr);
27891 var formData = new FormData();
27893 formData.append('returnHTML', 'NO');
27896 formData.append('crop', crop);
27899 formData.append(this.paramName, file, file.name);
27906 if(this.fireEvent('prepare', this, formData, options) != false){
27908 if(options.manually){
27912 this.xhr.send(formData);
27916 this.uploadCancel();
27919 uploadCancel : function()
27925 this.delegates = [];
27927 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27934 renderPreview : function(file)
27936 if(typeof(file.target) != 'undefined' && file.target){
27940 var previewEl = this.managerEl.createChild({
27942 cls : 'roo-document-manager-preview',
27946 tooltip : file[this.toolTipName],
27947 cls : 'roo-document-manager-thumb',
27948 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27953 html : '<i class="fa fa-times-circle"></i>'
27958 var close = previewEl.select('button.close', true).first();
27960 close.on('click', this.onRemove, this, file);
27962 file.target = previewEl;
27964 var image = previewEl.select('img', true).first();
27968 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27970 image.on('click', this.onClick, this, file);
27976 onPreviewLoad : function(file, image)
27978 if(typeof(file.target) == 'undefined' || !file.target){
27982 var width = image.dom.naturalWidth || image.dom.width;
27983 var height = image.dom.naturalHeight || image.dom.height;
27985 if(width > height){
27986 file.target.addClass('wide');
27990 file.target.addClass('tall');
27995 uploadFromSource : function(file, crop)
27997 this.xhr = new XMLHttpRequest();
27999 this.managerEl.createChild({
28001 cls : 'roo-document-manager-loading',
28005 tooltip : file.name,
28006 cls : 'roo-document-manager-thumb',
28007 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28013 this.xhr.open(this.method, this.url, true);
28016 "Accept": "application/json",
28017 "Cache-Control": "no-cache",
28018 "X-Requested-With": "XMLHttpRequest"
28021 for (var headerName in headers) {
28022 var headerValue = headers[headerName];
28024 this.xhr.setRequestHeader(headerName, headerValue);
28030 this.xhr.onload = function()
28032 _this.xhrOnLoad(_this.xhr);
28035 this.xhr.onerror = function()
28037 _this.xhrOnError(_this.xhr);
28040 var formData = new FormData();
28042 formData.append('returnHTML', 'NO');
28044 formData.append('crop', crop);
28046 if(typeof(file.filename) != 'undefined'){
28047 formData.append('filename', file.filename);
28050 if(typeof(file.mimetype) != 'undefined'){
28051 formData.append('mimetype', file.mimetype);
28054 if(this.fireEvent('prepare', this, formData) != false){
28055 this.xhr.send(formData);
28065 * @class Roo.bootstrap.DocumentViewer
28066 * @extends Roo.bootstrap.Component
28067 * Bootstrap DocumentViewer class
28068 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28069 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28072 * Create a new DocumentViewer
28073 * @param {Object} config The config object
28076 Roo.bootstrap.DocumentViewer = function(config){
28077 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28082 * Fire after initEvent
28083 * @param {Roo.bootstrap.DocumentViewer} this
28089 * @param {Roo.bootstrap.DocumentViewer} this
28094 * Fire after download button
28095 * @param {Roo.bootstrap.DocumentViewer} this
28100 * Fire after trash button
28101 * @param {Roo.bootstrap.DocumentViewer} this
28108 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28110 showDownload : true,
28114 getAutoCreate : function()
28118 cls : 'roo-document-viewer',
28122 cls : 'roo-document-viewer-body',
28126 cls : 'roo-document-viewer-thumb',
28130 cls : 'roo-document-viewer-image'
28138 cls : 'roo-document-viewer-footer',
28141 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28145 cls : 'btn-group roo-document-viewer-download',
28149 cls : 'btn btn-default',
28150 html : '<i class="fa fa-download"></i>'
28156 cls : 'btn-group roo-document-viewer-trash',
28160 cls : 'btn btn-default',
28161 html : '<i class="fa fa-trash"></i>'
28174 initEvents : function()
28176 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28177 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28179 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28180 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28182 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28183 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28185 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28186 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28188 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28189 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28191 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28192 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28194 this.bodyEl.on('click', this.onClick, this);
28195 this.downloadBtn.on('click', this.onDownload, this);
28196 this.trashBtn.on('click', this.onTrash, this);
28198 this.downloadBtn.hide();
28199 this.trashBtn.hide();
28201 if(this.showDownload){
28202 this.downloadBtn.show();
28205 if(this.showTrash){
28206 this.trashBtn.show();
28209 if(!this.showDownload && !this.showTrash) {
28210 this.footerEl.hide();
28215 initial : function()
28217 this.fireEvent('initial', this);
28221 onClick : function(e)
28223 e.preventDefault();
28225 this.fireEvent('click', this);
28228 onDownload : function(e)
28230 e.preventDefault();
28232 this.fireEvent('download', this);
28235 onTrash : function(e)
28237 e.preventDefault();
28239 this.fireEvent('trash', this);
28251 * @class Roo.bootstrap.NavProgressBar
28252 * @extends Roo.bootstrap.Component
28253 * Bootstrap NavProgressBar class
28256 * Create a new nav progress bar
28257 * @param {Object} config The config object
28260 Roo.bootstrap.NavProgressBar = function(config){
28261 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28263 this.bullets = this.bullets || [];
28265 // Roo.bootstrap.NavProgressBar.register(this);
28269 * Fires when the active item changes
28270 * @param {Roo.bootstrap.NavProgressBar} this
28271 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28272 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28279 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28284 getAutoCreate : function()
28286 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28290 cls : 'roo-navigation-bar-group',
28294 cls : 'roo-navigation-top-bar'
28298 cls : 'roo-navigation-bullets-bar',
28302 cls : 'roo-navigation-bar'
28309 cls : 'roo-navigation-bottom-bar'
28319 initEvents: function()
28324 onRender : function(ct, position)
28326 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28328 if(this.bullets.length){
28329 Roo.each(this.bullets, function(b){
28338 addItem : function(cfg)
28340 var item = new Roo.bootstrap.NavProgressItem(cfg);
28342 item.parentId = this.id;
28343 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28346 var top = new Roo.bootstrap.Element({
28348 cls : 'roo-navigation-bar-text'
28351 var bottom = new Roo.bootstrap.Element({
28353 cls : 'roo-navigation-bar-text'
28356 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28357 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28359 var topText = new Roo.bootstrap.Element({
28361 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28364 var bottomText = new Roo.bootstrap.Element({
28366 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28369 topText.onRender(top.el, null);
28370 bottomText.onRender(bottom.el, null);
28373 item.bottomEl = bottom;
28376 this.barItems.push(item);
28381 getActive : function()
28383 var active = false;
28385 Roo.each(this.barItems, function(v){
28387 if (!v.isActive()) {
28399 setActiveItem : function(item)
28403 Roo.each(this.barItems, function(v){
28404 if (v.rid == item.rid) {
28408 if (v.isActive()) {
28409 v.setActive(false);
28414 item.setActive(true);
28416 this.fireEvent('changed', this, item, prev);
28419 getBarItem: function(rid)
28423 Roo.each(this.barItems, function(e) {
28424 if (e.rid != rid) {
28435 indexOfItem : function(item)
28439 Roo.each(this.barItems, function(v, i){
28441 if (v.rid != item.rid) {
28452 setActiveNext : function()
28454 var i = this.indexOfItem(this.getActive());
28456 if (i > this.barItems.length) {
28460 this.setActiveItem(this.barItems[i+1]);
28463 setActivePrev : function()
28465 var i = this.indexOfItem(this.getActive());
28471 this.setActiveItem(this.barItems[i-1]);
28474 format : function()
28476 if(!this.barItems.length){
28480 var width = 100 / this.barItems.length;
28482 Roo.each(this.barItems, function(i){
28483 i.el.setStyle('width', width + '%');
28484 i.topEl.el.setStyle('width', width + '%');
28485 i.bottomEl.el.setStyle('width', width + '%');
28494 * Nav Progress Item
28499 * @class Roo.bootstrap.NavProgressItem
28500 * @extends Roo.bootstrap.Component
28501 * Bootstrap NavProgressItem class
28502 * @cfg {String} rid the reference id
28503 * @cfg {Boolean} active (true|false) Is item active default false
28504 * @cfg {Boolean} disabled (true|false) Is item active default false
28505 * @cfg {String} html
28506 * @cfg {String} position (top|bottom) text position default bottom
28507 * @cfg {String} icon show icon instead of number
28510 * Create a new NavProgressItem
28511 * @param {Object} config The config object
28513 Roo.bootstrap.NavProgressItem = function(config){
28514 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28519 * The raw click event for the entire grid.
28520 * @param {Roo.bootstrap.NavProgressItem} this
28521 * @param {Roo.EventObject} e
28528 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28534 position : 'bottom',
28537 getAutoCreate : function()
28539 var iconCls = 'roo-navigation-bar-item-icon';
28541 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28545 cls: 'roo-navigation-bar-item',
28555 cfg.cls += ' active';
28558 cfg.cls += ' disabled';
28564 disable : function()
28566 this.setDisabled(true);
28569 enable : function()
28571 this.setDisabled(false);
28574 initEvents: function()
28576 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28578 this.iconEl.on('click', this.onClick, this);
28581 onClick : function(e)
28583 e.preventDefault();
28589 if(this.fireEvent('click', this, e) === false){
28593 this.parent().setActiveItem(this);
28596 isActive: function ()
28598 return this.active;
28601 setActive : function(state)
28603 if(this.active == state){
28607 this.active = state;
28610 this.el.addClass('active');
28614 this.el.removeClass('active');
28619 setDisabled : function(state)
28621 if(this.disabled == state){
28625 this.disabled = state;
28628 this.el.addClass('disabled');
28632 this.el.removeClass('disabled');
28635 tooltipEl : function()
28637 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28650 * @class Roo.bootstrap.FieldLabel
28651 * @extends Roo.bootstrap.Component
28652 * Bootstrap FieldLabel class
28653 * @cfg {String} html contents of the element
28654 * @cfg {String} tag tag of the element default label
28655 * @cfg {String} cls class of the element
28656 * @cfg {String} target label target
28657 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28658 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28659 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28660 * @cfg {String} iconTooltip default "This field is required"
28663 * Create a new FieldLabel
28664 * @param {Object} config The config object
28667 Roo.bootstrap.FieldLabel = function(config){
28668 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28673 * Fires after the field has been marked as invalid.
28674 * @param {Roo.form.FieldLabel} this
28675 * @param {String} msg The validation message
28680 * Fires after the field has been validated with no errors.
28681 * @param {Roo.form.FieldLabel} this
28687 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28694 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28695 validClass : 'text-success fa fa-lg fa-check',
28696 iconTooltip : 'This field is required',
28698 getAutoCreate : function(){
28702 cls : 'roo-bootstrap-field-label ' + this.cls,
28708 tooltip : this.iconTooltip
28720 initEvents: function()
28722 Roo.bootstrap.Element.superclass.initEvents.call(this);
28724 this.iconEl = this.el.select('i', true).first();
28726 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28728 Roo.bootstrap.FieldLabel.register(this);
28732 * Mark this field as valid
28734 markValid : function()
28736 this.iconEl.show();
28738 this.iconEl.removeClass(this.invalidClass);
28740 this.iconEl.addClass(this.validClass);
28742 this.fireEvent('valid', this);
28746 * Mark this field as invalid
28747 * @param {String} msg The validation message
28749 markInvalid : function(msg)
28751 this.iconEl.show();
28753 this.iconEl.removeClass(this.validClass);
28755 this.iconEl.addClass(this.invalidClass);
28757 this.fireEvent('invalid', this, msg);
28763 Roo.apply(Roo.bootstrap.FieldLabel, {
28768 * register a FieldLabel Group
28769 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28771 register : function(label)
28773 if(this.groups.hasOwnProperty(label.target)){
28777 this.groups[label.target] = label;
28781 * fetch a FieldLabel Group based on the target
28782 * @param {string} target
28783 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28785 get: function(target) {
28786 if (typeof(this.groups[target]) == 'undefined') {
28790 return this.groups[target] ;
28799 * page DateSplitField.
28805 * @class Roo.bootstrap.DateSplitField
28806 * @extends Roo.bootstrap.Component
28807 * Bootstrap DateSplitField class
28808 * @cfg {string} fieldLabel - the label associated
28809 * @cfg {Number} labelWidth set the width of label (0-12)
28810 * @cfg {String} labelAlign (top|left)
28811 * @cfg {Boolean} dayAllowBlank (true|false) default false
28812 * @cfg {Boolean} monthAllowBlank (true|false) default false
28813 * @cfg {Boolean} yearAllowBlank (true|false) default false
28814 * @cfg {string} dayPlaceholder
28815 * @cfg {string} monthPlaceholder
28816 * @cfg {string} yearPlaceholder
28817 * @cfg {string} dayFormat default 'd'
28818 * @cfg {string} monthFormat default 'm'
28819 * @cfg {string} yearFormat default 'Y'
28823 * Create a new DateSplitField
28824 * @param {Object} config The config object
28827 Roo.bootstrap.DateSplitField = function(config){
28828 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28834 * getting the data of years
28835 * @param {Roo.bootstrap.DateSplitField} this
28836 * @param {Object} years
28841 * getting the data of days
28842 * @param {Roo.bootstrap.DateSplitField} this
28843 * @param {Object} days
28848 * Fires after the field has been marked as invalid.
28849 * @param {Roo.form.Field} this
28850 * @param {String} msg The validation message
28855 * Fires after the field has been validated with no errors.
28856 * @param {Roo.form.Field} this
28862 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28865 labelAlign : 'top',
28867 dayAllowBlank : false,
28868 monthAllowBlank : false,
28869 yearAllowBlank : false,
28870 dayPlaceholder : '',
28871 monthPlaceholder : '',
28872 yearPlaceholder : '',
28876 isFormField : true,
28878 getAutoCreate : function()
28882 cls : 'row roo-date-split-field-group',
28887 cls : 'form-hidden-field roo-date-split-field-group-value',
28893 if(this.fieldLabel){
28896 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28900 html : this.fieldLabel
28906 Roo.each(['day', 'month', 'year'], function(t){
28909 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28916 inputEl: function ()
28918 return this.el.select('.roo-date-split-field-group-value', true).first();
28921 onRender : function(ct, position)
28925 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28927 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28929 this.dayField = new Roo.bootstrap.ComboBox({
28930 allowBlank : this.dayAllowBlank,
28931 alwaysQuery : true,
28932 displayField : 'value',
28935 forceSelection : true,
28937 placeholder : this.dayPlaceholder,
28938 selectOnFocus : true,
28939 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28940 triggerAction : 'all',
28942 valueField : 'value',
28943 store : new Roo.data.SimpleStore({
28944 data : (function() {
28946 _this.fireEvent('days', _this, days);
28949 fields : [ 'value' ]
28952 select : function (_self, record, index)
28954 _this.setValue(_this.getValue());
28959 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28961 this.monthField = new Roo.bootstrap.MonthField({
28962 after : '<i class=\"fa fa-calendar\"></i>',
28963 allowBlank : this.monthAllowBlank,
28964 placeholder : this.monthPlaceholder,
28967 render : function (_self)
28969 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28970 e.preventDefault();
28974 select : function (_self, oldvalue, newvalue)
28976 _this.setValue(_this.getValue());
28981 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28983 this.yearField = new Roo.bootstrap.ComboBox({
28984 allowBlank : this.yearAllowBlank,
28985 alwaysQuery : true,
28986 displayField : 'value',
28989 forceSelection : true,
28991 placeholder : this.yearPlaceholder,
28992 selectOnFocus : true,
28993 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28994 triggerAction : 'all',
28996 valueField : 'value',
28997 store : new Roo.data.SimpleStore({
28998 data : (function() {
29000 _this.fireEvent('years', _this, years);
29003 fields : [ 'value' ]
29006 select : function (_self, record, index)
29008 _this.setValue(_this.getValue());
29013 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29016 setValue : function(v, format)
29018 this.inputEl.dom.value = v;
29020 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29022 var d = Date.parseDate(v, f);
29029 this.setDay(d.format(this.dayFormat));
29030 this.setMonth(d.format(this.monthFormat));
29031 this.setYear(d.format(this.yearFormat));
29038 setDay : function(v)
29040 this.dayField.setValue(v);
29041 this.inputEl.dom.value = this.getValue();
29046 setMonth : function(v)
29048 this.monthField.setValue(v, true);
29049 this.inputEl.dom.value = this.getValue();
29054 setYear : function(v)
29056 this.yearField.setValue(v);
29057 this.inputEl.dom.value = this.getValue();
29062 getDay : function()
29064 return this.dayField.getValue();
29067 getMonth : function()
29069 return this.monthField.getValue();
29072 getYear : function()
29074 return this.yearField.getValue();
29077 getValue : function()
29079 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29081 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29091 this.inputEl.dom.value = '';
29096 validate : function()
29098 var d = this.dayField.validate();
29099 var m = this.monthField.validate();
29100 var y = this.yearField.validate();
29105 (!this.dayAllowBlank && !d) ||
29106 (!this.monthAllowBlank && !m) ||
29107 (!this.yearAllowBlank && !y)
29112 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29121 this.markInvalid();
29126 markValid : function()
29129 var label = this.el.select('label', true).first();
29130 var icon = this.el.select('i.fa-star', true).first();
29136 this.fireEvent('valid', this);
29140 * Mark this field as invalid
29141 * @param {String} msg The validation message
29143 markInvalid : function(msg)
29146 var label = this.el.select('label', true).first();
29147 var icon = this.el.select('i.fa-star', true).first();
29149 if(label && !icon){
29150 this.el.select('.roo-date-split-field-label', true).createChild({
29152 cls : 'text-danger fa fa-lg fa-star',
29153 tooltip : 'This field is required',
29154 style : 'margin-right:5px;'
29158 this.fireEvent('invalid', this, msg);
29161 clearInvalid : function()
29163 var label = this.el.select('label', true).first();
29164 var icon = this.el.select('i.fa-star', true).first();
29170 this.fireEvent('valid', this);
29173 getName: function()
29183 * http://masonry.desandro.com
29185 * The idea is to render all the bricks based on vertical width...
29187 * The original code extends 'outlayer' - we might need to use that....
29193 * @class Roo.bootstrap.LayoutMasonry
29194 * @extends Roo.bootstrap.Component
29195 * Bootstrap Layout Masonry class
29198 * Create a new Element
29199 * @param {Object} config The config object
29202 Roo.bootstrap.LayoutMasonry = function(config){
29203 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29209 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29212 * @cfg {Boolean} isLayoutInstant = no animation?
29214 isLayoutInstant : false, // needed?
29217 * @cfg {Number} boxWidth width of the columns
29222 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29227 * @cfg {Number} padWidth padding below box..
29232 * @cfg {Number} gutter gutter width..
29237 * @cfg {Number} maxCols maximum number of columns
29243 * @cfg {Boolean} isAutoInitial defalut true
29245 isAutoInitial : true,
29250 * @cfg {Boolean} isHorizontal defalut false
29252 isHorizontal : false,
29254 currentSize : null,
29260 bricks: null, //CompositeElement
29264 _isLayoutInited : false,
29266 // isAlternative : false, // only use for vertical layout...
29269 * @cfg {Number} alternativePadWidth padding below box..
29271 alternativePadWidth : 50,
29273 getAutoCreate : function(){
29277 cls: 'blog-masonary-wrapper ' + this.cls,
29279 cls : 'mas-boxes masonary'
29286 getChildContainer: function( )
29288 if (this.boxesEl) {
29289 return this.boxesEl;
29292 this.boxesEl = this.el.select('.mas-boxes').first();
29294 return this.boxesEl;
29298 initEvents : function()
29302 if(this.isAutoInitial){
29303 Roo.log('hook children rendered');
29304 this.on('childrenrendered', function() {
29305 Roo.log('children rendered');
29311 initial : function()
29313 this.currentSize = this.el.getBox(true);
29315 Roo.EventManager.onWindowResize(this.resize, this);
29317 if(!this.isAutoInitial){
29325 //this.layout.defer(500,this);
29329 resize : function()
29333 var cs = this.el.getBox(true);
29335 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29336 Roo.log("no change in with or X");
29340 this.currentSize = cs;
29346 layout : function()
29348 this._resetLayout();
29350 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29352 this.layoutItems( isInstant );
29354 this._isLayoutInited = true;
29358 _resetLayout : function()
29360 if(this.isHorizontal){
29361 this.horizontalMeasureColumns();
29365 this.verticalMeasureColumns();
29369 verticalMeasureColumns : function()
29371 this.getContainerWidth();
29373 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29374 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29378 var boxWidth = this.boxWidth + this.padWidth;
29380 if(this.containerWidth < this.boxWidth){
29381 boxWidth = this.containerWidth
29384 var containerWidth = this.containerWidth;
29386 var cols = Math.floor(containerWidth / boxWidth);
29388 this.cols = Math.max( cols, 1 );
29390 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29392 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29394 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29396 this.colWidth = boxWidth + avail - this.padWidth;
29398 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29399 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29402 horizontalMeasureColumns : function()
29404 this.getContainerWidth();
29406 var boxWidth = this.boxWidth;
29408 if(this.containerWidth < boxWidth){
29409 boxWidth = this.containerWidth;
29412 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29414 this.el.setHeight(boxWidth);
29418 getContainerWidth : function()
29420 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29423 layoutItems : function( isInstant )
29425 var items = Roo.apply([], this.bricks);
29427 if(this.isHorizontal){
29428 this._horizontalLayoutItems( items , isInstant );
29432 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29433 // this._verticalAlternativeLayoutItems( items , isInstant );
29437 this._verticalLayoutItems( items , isInstant );
29441 _verticalLayoutItems : function ( items , isInstant)
29443 if ( !items || !items.length ) {
29448 ['xs', 'xs', 'xs', 'tall'],
29449 ['xs', 'xs', 'tall'],
29450 ['xs', 'xs', 'sm'],
29451 ['xs', 'xs', 'xs'],
29457 ['sm', 'xs', 'xs'],
29461 ['tall', 'xs', 'xs', 'xs'],
29462 ['tall', 'xs', 'xs'],
29474 Roo.each(items, function(item, k){
29476 switch (item.size) {
29477 // these layouts take up a full box,
29488 boxes.push([item]);
29511 var filterPattern = function(box, length)
29519 var pattern = box.slice(0, length);
29523 Roo.each(pattern, function(i){
29524 format.push(i.size);
29527 Roo.each(standard, function(s){
29529 if(String(s) != String(format)){
29538 if(!match && length == 1){
29543 filterPattern(box, length - 1);
29547 queue.push(pattern);
29549 box = box.slice(length, box.length);
29551 filterPattern(box, 4);
29557 Roo.each(boxes, function(box, k){
29563 if(box.length == 1){
29568 filterPattern(box, 4);
29572 this._processVerticalLayoutQueue( queue, isInstant );
29576 // _verticalAlternativeLayoutItems : function( items , isInstant )
29578 // if ( !items || !items.length ) {
29582 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29586 _horizontalLayoutItems : function ( items , isInstant)
29588 if ( !items || !items.length || items.length < 3) {
29594 var eItems = items.slice(0, 3);
29596 items = items.slice(3, items.length);
29599 ['xs', 'xs', 'xs', 'wide'],
29600 ['xs', 'xs', 'wide'],
29601 ['xs', 'xs', 'sm'],
29602 ['xs', 'xs', 'xs'],
29608 ['sm', 'xs', 'xs'],
29612 ['wide', 'xs', 'xs', 'xs'],
29613 ['wide', 'xs', 'xs'],
29626 Roo.each(items, function(item, k){
29628 switch (item.size) {
29639 boxes.push([item]);
29663 var filterPattern = function(box, length)
29671 var pattern = box.slice(0, length);
29675 Roo.each(pattern, function(i){
29676 format.push(i.size);
29679 Roo.each(standard, function(s){
29681 if(String(s) != String(format)){
29690 if(!match && length == 1){
29695 filterPattern(box, length - 1);
29699 queue.push(pattern);
29701 box = box.slice(length, box.length);
29703 filterPattern(box, 4);
29709 Roo.each(boxes, function(box, k){
29715 if(box.length == 1){
29720 filterPattern(box, 4);
29727 var pos = this.el.getBox(true);
29731 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29733 var hit_end = false;
29735 Roo.each(queue, function(box){
29739 Roo.each(box, function(b){
29741 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29751 Roo.each(box, function(b){
29753 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29756 mx = Math.max(mx, b.x);
29760 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29764 Roo.each(box, function(b){
29766 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29780 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29783 /** Sets position of item in DOM
29784 * @param {Element} item
29785 * @param {Number} x - horizontal position
29786 * @param {Number} y - vertical position
29787 * @param {Boolean} isInstant - disables transitions
29789 _processVerticalLayoutQueue : function( queue, isInstant )
29791 var pos = this.el.getBox(true);
29796 for (var i = 0; i < this.cols; i++){
29800 Roo.each(queue, function(box, k){
29802 var col = k % this.cols;
29804 Roo.each(box, function(b,kk){
29806 b.el.position('absolute');
29808 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29809 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29811 if(b.size == 'md-left' || b.size == 'md-right'){
29812 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29813 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29816 b.el.setWidth(width);
29817 b.el.setHeight(height);
29819 b.el.select('iframe',true).setSize(width,height);
29823 for (var i = 0; i < this.cols; i++){
29825 if(maxY[i] < maxY[col]){
29830 col = Math.min(col, i);
29834 x = pos.x + col * (this.colWidth + this.padWidth);
29838 var positions = [];
29840 switch (box.length){
29842 positions = this.getVerticalOneBoxColPositions(x, y, box);
29845 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29848 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29851 positions = this.getVerticalFourBoxColPositions(x, y, box);
29857 Roo.each(box, function(b,kk){
29859 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29861 var sz = b.el.getSize();
29863 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29871 for (var i = 0; i < this.cols; i++){
29872 mY = Math.max(mY, maxY[i]);
29875 this.el.setHeight(mY - pos.y);
29879 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29881 // var pos = this.el.getBox(true);
29884 // var maxX = pos.right;
29886 // var maxHeight = 0;
29888 // Roo.each(items, function(item, k){
29892 // item.el.position('absolute');
29894 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29896 // item.el.setWidth(width);
29898 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29900 // item.el.setHeight(height);
29903 // item.el.setXY([x, y], isInstant ? false : true);
29905 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29908 // y = y + height + this.alternativePadWidth;
29910 // maxHeight = maxHeight + height + this.alternativePadWidth;
29914 // this.el.setHeight(maxHeight);
29918 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29920 var pos = this.el.getBox(true);
29925 var maxX = pos.right;
29927 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29929 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29931 Roo.each(queue, function(box, k){
29933 Roo.each(box, function(b, kk){
29935 b.el.position('absolute');
29937 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29938 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29940 if(b.size == 'md-left' || b.size == 'md-right'){
29941 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29942 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29945 b.el.setWidth(width);
29946 b.el.setHeight(height);
29954 var positions = [];
29956 switch (box.length){
29958 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29961 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29964 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29967 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29973 Roo.each(box, function(b,kk){
29975 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29977 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29985 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29987 Roo.each(eItems, function(b,k){
29989 b.size = (k == 0) ? 'sm' : 'xs';
29990 b.x = (k == 0) ? 2 : 1;
29991 b.y = (k == 0) ? 2 : 1;
29993 b.el.position('absolute');
29995 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29997 b.el.setWidth(width);
29999 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30001 b.el.setHeight(height);
30005 var positions = [];
30008 x : maxX - this.unitWidth * 2 - this.gutter,
30013 x : maxX - this.unitWidth,
30014 y : minY + (this.unitWidth + this.gutter) * 2
30018 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30022 Roo.each(eItems, function(b,k){
30024 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30030 getVerticalOneBoxColPositions : function(x, y, box)
30034 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30036 if(box[0].size == 'md-left'){
30040 if(box[0].size == 'md-right'){
30045 x : x + (this.unitWidth + this.gutter) * rand,
30052 getVerticalTwoBoxColPositions : function(x, y, box)
30056 if(box[0].size == 'xs'){
30060 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30064 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30078 x : x + (this.unitWidth + this.gutter) * 2,
30079 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30086 getVerticalThreeBoxColPositions : function(x, y, box)
30090 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30098 x : x + (this.unitWidth + this.gutter) * 1,
30103 x : x + (this.unitWidth + this.gutter) * 2,
30111 if(box[0].size == 'xs' && box[1].size == 'xs'){
30120 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30124 x : x + (this.unitWidth + this.gutter) * 1,
30138 x : x + (this.unitWidth + this.gutter) * 2,
30143 x : x + (this.unitWidth + this.gutter) * 2,
30144 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30151 getVerticalFourBoxColPositions : function(x, y, box)
30155 if(box[0].size == 'xs'){
30164 y : y + (this.unitHeight + this.gutter) * 1
30169 y : y + (this.unitHeight + this.gutter) * 2
30173 x : x + (this.unitWidth + this.gutter) * 1,
30187 x : x + (this.unitWidth + this.gutter) * 2,
30192 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30193 y : y + (this.unitHeight + this.gutter) * 1
30197 x : x + (this.unitWidth + this.gutter) * 2,
30198 y : y + (this.unitWidth + this.gutter) * 2
30205 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30209 if(box[0].size == 'md-left'){
30211 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30218 if(box[0].size == 'md-right'){
30220 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30221 y : minY + (this.unitWidth + this.gutter) * 1
30227 var rand = Math.floor(Math.random() * (4 - box[0].y));
30230 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30231 y : minY + (this.unitWidth + this.gutter) * rand
30238 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30242 if(box[0].size == 'xs'){
30245 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30250 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30251 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30259 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30264 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30265 y : minY + (this.unitWidth + this.gutter) * 2
30272 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30276 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30279 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30284 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30285 y : minY + (this.unitWidth + this.gutter) * 1
30289 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30290 y : minY + (this.unitWidth + this.gutter) * 2
30297 if(box[0].size == 'xs' && box[1].size == 'xs'){
30300 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30305 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30310 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30311 y : minY + (this.unitWidth + this.gutter) * 1
30319 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30324 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30325 y : minY + (this.unitWidth + this.gutter) * 2
30329 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30330 y : minY + (this.unitWidth + this.gutter) * 2
30337 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30341 if(box[0].size == 'xs'){
30344 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30349 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30354 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),
30359 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30360 y : minY + (this.unitWidth + this.gutter) * 1
30368 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30373 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30374 y : minY + (this.unitWidth + this.gutter) * 2
30378 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30379 y : minY + (this.unitWidth + this.gutter) * 2
30383 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),
30384 y : minY + (this.unitWidth + this.gutter) * 2
30398 * http://masonry.desandro.com
30400 * The idea is to render all the bricks based on vertical width...
30402 * The original code extends 'outlayer' - we might need to use that....
30408 * @class Roo.bootstrap.LayoutMasonryAuto
30409 * @extends Roo.bootstrap.Component
30410 * Bootstrap Layout Masonry class
30413 * Create a new Element
30414 * @param {Object} config The config object
30417 Roo.bootstrap.LayoutMasonryAuto = function(config){
30418 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30421 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30424 * @cfg {Boolean} isFitWidth - resize the width..
30426 isFitWidth : false, // options..
30428 * @cfg {Boolean} isOriginLeft = left align?
30430 isOriginLeft : true,
30432 * @cfg {Boolean} isOriginTop = top align?
30434 isOriginTop : false,
30436 * @cfg {Boolean} isLayoutInstant = no animation?
30438 isLayoutInstant : false, // needed?
30440 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30442 isResizingContainer : true,
30444 * @cfg {Number} columnWidth width of the columns
30450 * @cfg {Number} maxCols maximum number of columns
30455 * @cfg {Number} padHeight padding below box..
30461 * @cfg {Boolean} isAutoInitial defalut true
30464 isAutoInitial : true,
30470 initialColumnWidth : 0,
30471 currentSize : null,
30473 colYs : null, // array.
30480 bricks: null, //CompositeElement
30481 cols : 0, // array?
30482 // element : null, // wrapped now this.el
30483 _isLayoutInited : null,
30486 getAutoCreate : function(){
30490 cls: 'blog-masonary-wrapper ' + this.cls,
30492 cls : 'mas-boxes masonary'
30499 getChildContainer: function( )
30501 if (this.boxesEl) {
30502 return this.boxesEl;
30505 this.boxesEl = this.el.select('.mas-boxes').first();
30507 return this.boxesEl;
30511 initEvents : function()
30515 if(this.isAutoInitial){
30516 Roo.log('hook children rendered');
30517 this.on('childrenrendered', function() {
30518 Roo.log('children rendered');
30525 initial : function()
30527 this.reloadItems();
30529 this.currentSize = this.el.getBox(true);
30531 /// was window resize... - let's see if this works..
30532 Roo.EventManager.onWindowResize(this.resize, this);
30534 if(!this.isAutoInitial){
30539 this.layout.defer(500,this);
30542 reloadItems: function()
30544 this.bricks = this.el.select('.masonry-brick', true);
30546 this.bricks.each(function(b) {
30547 //Roo.log(b.getSize());
30548 if (!b.attr('originalwidth')) {
30549 b.attr('originalwidth', b.getSize().width);
30554 Roo.log(this.bricks.elements.length);
30557 resize : function()
30560 var cs = this.el.getBox(true);
30562 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30563 Roo.log("no change in with or X");
30566 this.currentSize = cs;
30570 layout : function()
30573 this._resetLayout();
30574 //this._manageStamps();
30576 // don't animate first layout
30577 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30578 this.layoutItems( isInstant );
30580 // flag for initalized
30581 this._isLayoutInited = true;
30584 layoutItems : function( isInstant )
30586 //var items = this._getItemsForLayout( this.items );
30587 // original code supports filtering layout items.. we just ignore it..
30589 this._layoutItems( this.bricks , isInstant );
30591 this._postLayout();
30593 _layoutItems : function ( items , isInstant)
30595 //this.fireEvent( 'layout', this, items );
30598 if ( !items || !items.elements.length ) {
30599 // no items, emit event with empty array
30604 items.each(function(item) {
30605 Roo.log("layout item");
30607 // get x/y object from method
30608 var position = this._getItemLayoutPosition( item );
30610 position.item = item;
30611 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30612 queue.push( position );
30615 this._processLayoutQueue( queue );
30617 /** Sets position of item in DOM
30618 * @param {Element} item
30619 * @param {Number} x - horizontal position
30620 * @param {Number} y - vertical position
30621 * @param {Boolean} isInstant - disables transitions
30623 _processLayoutQueue : function( queue )
30625 for ( var i=0, len = queue.length; i < len; i++ ) {
30626 var obj = queue[i];
30627 obj.item.position('absolute');
30628 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30634 * Any logic you want to do after each layout,
30635 * i.e. size the container
30637 _postLayout : function()
30639 this.resizeContainer();
30642 resizeContainer : function()
30644 if ( !this.isResizingContainer ) {
30647 var size = this._getContainerSize();
30649 this.el.setSize(size.width,size.height);
30650 this.boxesEl.setSize(size.width,size.height);
30656 _resetLayout : function()
30658 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30659 this.colWidth = this.el.getWidth();
30660 //this.gutter = this.el.getWidth();
30662 this.measureColumns();
30668 this.colYs.push( 0 );
30674 measureColumns : function()
30676 this.getContainerWidth();
30677 // if columnWidth is 0, default to outerWidth of first item
30678 if ( !this.columnWidth ) {
30679 var firstItem = this.bricks.first();
30680 Roo.log(firstItem);
30681 this.columnWidth = this.containerWidth;
30682 if (firstItem && firstItem.attr('originalwidth') ) {
30683 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30685 // columnWidth fall back to item of first element
30686 Roo.log("set column width?");
30687 this.initialColumnWidth = this.columnWidth ;
30689 // if first elem has no width, default to size of container
30694 if (this.initialColumnWidth) {
30695 this.columnWidth = this.initialColumnWidth;
30700 // column width is fixed at the top - however if container width get's smaller we should
30703 // this bit calcs how man columns..
30705 var columnWidth = this.columnWidth += this.gutter;
30707 // calculate columns
30708 var containerWidth = this.containerWidth + this.gutter;
30710 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30711 // fix rounding errors, typically with gutters
30712 var excess = columnWidth - containerWidth % columnWidth;
30715 // if overshoot is less than a pixel, round up, otherwise floor it
30716 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30717 cols = Math[ mathMethod ]( cols );
30718 this.cols = Math.max( cols, 1 );
30719 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30721 // padding positioning..
30722 var totalColWidth = this.cols * this.columnWidth;
30723 var padavail = this.containerWidth - totalColWidth;
30724 // so for 2 columns - we need 3 'pads'
30726 var padNeeded = (1+this.cols) * this.padWidth;
30728 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30730 this.columnWidth += padExtra
30731 //this.padWidth = Math.floor(padavail / ( this.cols));
30733 // adjust colum width so that padding is fixed??
30735 // we have 3 columns ... total = width * 3
30736 // we have X left over... that should be used by
30738 //if (this.expandC) {
30746 getContainerWidth : function()
30748 /* // container is parent if fit width
30749 var container = this.isFitWidth ? this.element.parentNode : this.element;
30750 // check that this.size and size are there
30751 // IE8 triggers resize on body size change, so they might not be
30753 var size = getSize( container ); //FIXME
30754 this.containerWidth = size && size.innerWidth; //FIXME
30757 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30761 _getItemLayoutPosition : function( item ) // what is item?
30763 // we resize the item to our columnWidth..
30765 item.setWidth(this.columnWidth);
30766 item.autoBoxAdjust = false;
30768 var sz = item.getSize();
30770 // how many columns does this brick span
30771 var remainder = this.containerWidth % this.columnWidth;
30773 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30774 // round if off by 1 pixel, otherwise use ceil
30775 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30776 colSpan = Math.min( colSpan, this.cols );
30778 // normally this should be '1' as we dont' currently allow multi width columns..
30780 var colGroup = this._getColGroup( colSpan );
30781 // get the minimum Y value from the columns
30782 var minimumY = Math.min.apply( Math, colGroup );
30783 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30785 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30787 // position the brick
30789 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30790 y: this.currentSize.y + minimumY + this.padHeight
30794 // apply setHeight to necessary columns
30795 var setHeight = minimumY + sz.height + this.padHeight;
30796 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30798 var setSpan = this.cols + 1 - colGroup.length;
30799 for ( var i = 0; i < setSpan; i++ ) {
30800 this.colYs[ shortColIndex + i ] = setHeight ;
30807 * @param {Number} colSpan - number of columns the element spans
30808 * @returns {Array} colGroup
30810 _getColGroup : function( colSpan )
30812 if ( colSpan < 2 ) {
30813 // if brick spans only one column, use all the column Ys
30818 // how many different places could this brick fit horizontally
30819 var groupCount = this.cols + 1 - colSpan;
30820 // for each group potential horizontal position
30821 for ( var i = 0; i < groupCount; i++ ) {
30822 // make an array of colY values for that one group
30823 var groupColYs = this.colYs.slice( i, i + colSpan );
30824 // and get the max value of the array
30825 colGroup[i] = Math.max.apply( Math, groupColYs );
30830 _manageStamp : function( stamp )
30832 var stampSize = stamp.getSize();
30833 var offset = stamp.getBox();
30834 // get the columns that this stamp affects
30835 var firstX = this.isOriginLeft ? offset.x : offset.right;
30836 var lastX = firstX + stampSize.width;
30837 var firstCol = Math.floor( firstX / this.columnWidth );
30838 firstCol = Math.max( 0, firstCol );
30840 var lastCol = Math.floor( lastX / this.columnWidth );
30841 // lastCol should not go over if multiple of columnWidth #425
30842 lastCol -= lastX % this.columnWidth ? 0 : 1;
30843 lastCol = Math.min( this.cols - 1, lastCol );
30845 // set colYs to bottom of the stamp
30846 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30849 for ( var i = firstCol; i <= lastCol; i++ ) {
30850 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30855 _getContainerSize : function()
30857 this.maxY = Math.max.apply( Math, this.colYs );
30862 if ( this.isFitWidth ) {
30863 size.width = this._getContainerFitWidth();
30869 _getContainerFitWidth : function()
30871 var unusedCols = 0;
30872 // count unused columns
30875 if ( this.colYs[i] !== 0 ) {
30880 // fit container to columns that have been used
30881 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30884 needsResizeLayout : function()
30886 var previousWidth = this.containerWidth;
30887 this.getContainerWidth();
30888 return previousWidth !== this.containerWidth;
30903 * @class Roo.bootstrap.MasonryBrick
30904 * @extends Roo.bootstrap.Component
30905 * Bootstrap MasonryBrick class
30908 * Create a new MasonryBrick
30909 * @param {Object} config The config object
30912 Roo.bootstrap.MasonryBrick = function(config){
30913 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30919 * When a MasonryBrick is clcik
30920 * @param {Roo.bootstrap.MasonryBrick} this
30921 * @param {Roo.EventObject} e
30927 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30930 * @cfg {String} title
30934 * @cfg {String} html
30938 * @cfg {String} bgimage
30942 * @cfg {String} videourl
30946 * @cfg {String} cls
30950 * @cfg {String} href
30954 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30959 * @cfg {String} (center|bottom) placetitle
30964 * @cfg {Boolean} isFitContainer defalut true
30966 isFitContainer : true,
30969 * @cfg {Boolean} preventDefault defalut false
30971 preventDefault : false,
30973 getAutoCreate : function()
30975 if(!this.isFitContainer){
30976 return this.getSplitAutoCreate();
30979 var cls = 'masonry-brick masonry-brick-full';
30981 if(this.href.length){
30982 cls += ' masonry-brick-link';
30985 if(this.bgimage.length){
30986 cls += ' masonry-brick-image';
30989 if(!this.html.length){
30990 cls += ' enable-mask';
30994 cls += ' masonry-' + this.size + '-brick';
30997 if(this.placetitle.length){
30999 switch (this.placetitle) {
31001 cls += ' masonry-center-title';
31004 cls += ' masonry-bottom-title';
31011 if(!this.html.length && !this.bgimage.length){
31012 cls += ' masonry-center-title';
31015 if(!this.html.length && this.bgimage.length){
31016 cls += ' masonry-bottom-title';
31021 cls += ' ' + this.cls;
31025 tag: (this.href.length) ? 'a' : 'div',
31030 cls: 'masonry-brick-paragraph',
31036 if(this.href.length){
31037 cfg.href = this.href;
31040 var cn = cfg.cn[0].cn;
31042 if(this.title.length){
31045 cls: 'masonry-brick-title',
31050 if(this.html.length){
31053 cls: 'masonry-brick-text',
31057 if (!this.title.length && !this.html.length) {
31058 cfg.cn[0].cls += ' hide';
31061 if(this.bgimage.length){
31064 cls: 'masonry-brick-image-view',
31069 if(this.videourl.length){
31070 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31071 // youtube support only?
31074 cls: 'masonry-brick-image-view',
31077 allowfullscreen : true
31085 cls: 'masonry-brick-mask'
31092 getSplitAutoCreate : function()
31094 var cls = 'masonry-brick masonry-brick-split';
31096 if(this.href.length){
31097 cls += ' masonry-brick-link';
31100 if(this.bgimage.length){
31101 cls += ' masonry-brick-image';
31105 cls += ' masonry-' + this.size + '-brick';
31108 switch (this.placetitle) {
31110 cls += ' masonry-center-title';
31113 cls += ' masonry-bottom-title';
31116 if(!this.bgimage.length){
31117 cls += ' masonry-center-title';
31120 if(this.bgimage.length){
31121 cls += ' masonry-bottom-title';
31127 cls += ' ' + this.cls;
31131 tag: (this.href.length) ? 'a' : 'div',
31136 cls: 'masonry-brick-split-head',
31140 cls: 'masonry-brick-paragraph',
31147 cls: 'masonry-brick-split-body',
31153 if(this.href.length){
31154 cfg.href = this.href;
31157 if(this.title.length){
31158 cfg.cn[0].cn[0].cn.push({
31160 cls: 'masonry-brick-title',
31165 if(this.html.length){
31166 cfg.cn[1].cn.push({
31168 cls: 'masonry-brick-text',
31173 if(this.bgimage.length){
31174 cfg.cn[0].cn.push({
31176 cls: 'masonry-brick-image-view',
31181 if(this.videourl.length){
31182 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31183 // youtube support only?
31184 cfg.cn[0].cn.cn.push({
31186 cls: 'masonry-brick-image-view',
31189 allowfullscreen : true
31196 initEvents: function()
31198 switch (this.size) {
31231 this.el.on('touchstart', this.onTouchStart, this);
31232 this.el.on('touchmove', this.onTouchMove, this);
31233 this.el.on('touchend', this.onTouchEnd, this);
31234 this.el.on('contextmenu', this.onContextMenu, this);
31236 this.el.on('mouseenter' ,this.enter, this);
31237 this.el.on('mouseleave', this.leave, this);
31238 this.el.on('click', this.onClick, this);
31241 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31242 this.parent().bricks.push(this);
31247 onClick: function(e, el)
31249 var time = this.endTimer - this.startTimer;
31253 e.preventDefault();
31258 if(!this.preventDefault){
31262 e.preventDefault();
31263 this.fireEvent('click', this);
31266 enter: function(e, el)
31268 e.preventDefault();
31270 if(!this.isFitContainer){
31274 if(this.bgimage.length && this.html.length){
31275 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31279 leave: function(e, el)
31281 e.preventDefault();
31283 if(!this.isFitContainer){
31287 if(this.bgimage.length && this.html.length){
31288 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31292 onTouchStart: function(e, el)
31294 // e.preventDefault();
31296 this.touchmoved = false;
31298 if(!this.isFitContainer){
31302 if(!this.bgimage.length || !this.html.length){
31306 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31308 this.timer = new Date().getTime();
31312 onTouchMove: function(e, el)
31314 this.touchmoved = true;
31317 onContextMenu : function(e,el)
31319 e.preventDefault();
31320 e.stopPropagation();
31324 onTouchEnd: function(e, el)
31326 // e.preventDefault();
31328 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31335 if(!this.bgimage.length || !this.html.length){
31337 if(this.href.length){
31338 window.location.href = this.href;
31344 if(!this.isFitContainer){
31348 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31350 window.location.href = this.href;
31365 * @class Roo.bootstrap.Brick
31366 * @extends Roo.bootstrap.Component
31367 * Bootstrap Brick class
31370 * Create a new Brick
31371 * @param {Object} config The config object
31374 Roo.bootstrap.Brick = function(config){
31375 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31381 * When a Brick is click
31382 * @param {Roo.bootstrap.Brick} this
31383 * @param {Roo.EventObject} e
31389 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31392 * @cfg {String} title
31396 * @cfg {String} html
31400 * @cfg {String} bgimage
31404 * @cfg {String} cls
31408 * @cfg {String} href
31412 * @cfg {String} video
31416 * @cfg {Boolean} square
31420 getAutoCreate : function()
31422 var cls = 'roo-brick';
31424 if(this.href.length){
31425 cls += ' roo-brick-link';
31428 if(this.bgimage.length){
31429 cls += ' roo-brick-image';
31432 if(!this.html.length && !this.bgimage.length){
31433 cls += ' roo-brick-center-title';
31436 if(!this.html.length && this.bgimage.length){
31437 cls += ' roo-brick-bottom-title';
31441 cls += ' ' + this.cls;
31445 tag: (this.href.length) ? 'a' : 'div',
31450 cls: 'roo-brick-paragraph',
31456 if(this.href.length){
31457 cfg.href = this.href;
31460 var cn = cfg.cn[0].cn;
31462 if(this.title.length){
31465 cls: 'roo-brick-title',
31470 if(this.html.length){
31473 cls: 'roo-brick-text',
31480 if(this.bgimage.length){
31483 cls: 'roo-brick-image-view',
31491 initEvents: function()
31493 if(this.title.length || this.html.length){
31494 this.el.on('mouseenter' ,this.enter, this);
31495 this.el.on('mouseleave', this.leave, this);
31499 Roo.EventManager.onWindowResize(this.resize, this);
31504 resize : function()
31506 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31508 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31510 if(this.bgimage.length){
31511 var image = this.el.select('.roo-brick-image-view', true).first();
31512 image.setWidth(paragraph.getWidth());
31513 image.setHeight(paragraph.getWidth());
31515 this.el.setHeight(paragraph.getWidth());
31521 enter: function(e, el)
31523 e.preventDefault();
31525 if(this.bgimage.length){
31526 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31527 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31531 leave: function(e, el)
31533 e.preventDefault();
31535 if(this.bgimage.length){
31536 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31537 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31553 * @class Roo.bootstrap.NumberField
31554 * @extends Roo.bootstrap.Input
31555 * Bootstrap NumberField class
31561 * Create a new NumberField
31562 * @param {Object} config The config object
31565 Roo.bootstrap.NumberField = function(config){
31566 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31569 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31572 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31574 allowDecimals : true,
31576 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31578 decimalSeparator : ".",
31580 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31582 decimalPrecision : 2,
31584 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31586 allowNegative : true,
31588 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31590 minValue : Number.NEGATIVE_INFINITY,
31592 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31594 maxValue : Number.MAX_VALUE,
31596 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31598 minText : "The minimum value for this field is {0}",
31600 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31602 maxText : "The maximum value for this field is {0}",
31604 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31605 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31607 nanText : "{0} is not a valid number",
31609 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31614 initEvents : function()
31616 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31618 var allowed = "0123456789";
31620 if(this.allowDecimals){
31621 allowed += this.decimalSeparator;
31624 if(this.allowNegative){
31628 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31630 var keyPress = function(e){
31632 var k = e.getKey();
31634 var c = e.getCharCode();
31637 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31638 allowed.indexOf(String.fromCharCode(c)) === -1
31644 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31648 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31653 this.el.on("keypress", keyPress, this);
31656 validateValue : function(value)
31659 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31663 var num = this.parseValue(value);
31666 this.markInvalid(String.format(this.nanText, value));
31670 if(num < this.minValue){
31671 this.markInvalid(String.format(this.minText, this.minValue));
31675 if(num > this.maxValue){
31676 this.markInvalid(String.format(this.maxText, this.maxValue));
31683 getValue : function()
31685 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31688 parseValue : function(value)
31690 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31691 return isNaN(value) ? '' : value;
31694 fixPrecision : function(value)
31696 var nan = isNaN(value);
31698 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31699 return nan ? '' : value;
31701 return parseFloat(value).toFixed(this.decimalPrecision);
31704 setValue : function(v)
31706 v = this.fixPrecision(v);
31707 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31710 decimalPrecisionFcn : function(v)
31712 return Math.floor(v);
31715 beforeBlur : function()
31721 var v = this.parseValue(this.getRawValue());
31736 * @class Roo.bootstrap.DocumentSlider
31737 * @extends Roo.bootstrap.Component
31738 * Bootstrap DocumentSlider class
31741 * Create a new DocumentViewer
31742 * @param {Object} config The config object
31745 Roo.bootstrap.DocumentSlider = function(config){
31746 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31753 * Fire after initEvent
31754 * @param {Roo.bootstrap.DocumentSlider} this
31759 * Fire after update
31760 * @param {Roo.bootstrap.DocumentSlider} this
31766 * @param {Roo.bootstrap.DocumentSlider} this
31772 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31778 getAutoCreate : function()
31782 cls : 'roo-document-slider',
31786 cls : 'roo-document-slider-header',
31790 cls : 'roo-document-slider-header-title'
31796 cls : 'roo-document-slider-body',
31800 cls : 'roo-document-slider-prev',
31804 cls : 'fa fa-chevron-left'
31810 cls : 'roo-document-slider-thumb',
31814 cls : 'roo-document-slider-image'
31820 cls : 'roo-document-slider-next',
31824 cls : 'fa fa-chevron-right'
31836 initEvents : function()
31838 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31839 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31841 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31842 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31844 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31845 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31847 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31848 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31850 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31851 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31853 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31854 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31856 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31857 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31859 this.thumbEl.on('click', this.onClick, this);
31861 this.prevIndicator.on('click', this.prev, this);
31863 this.nextIndicator.on('click', this.next, this);
31867 initial : function()
31869 if(this.files.length){
31870 this.indicator = 1;
31874 this.fireEvent('initial', this);
31877 update : function()
31879 this.imageEl.attr('src', this.files[this.indicator - 1]);
31881 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31883 this.prevIndicator.show();
31885 if(this.indicator == 1){
31886 this.prevIndicator.hide();
31889 this.nextIndicator.show();
31891 if(this.indicator == this.files.length){
31892 this.nextIndicator.hide();
31895 this.thumbEl.scrollTo('top');
31897 this.fireEvent('update', this);
31900 onClick : function(e)
31902 e.preventDefault();
31904 this.fireEvent('click', this);
31909 e.preventDefault();
31911 this.indicator = Math.max(1, this.indicator - 1);
31918 e.preventDefault();
31920 this.indicator = Math.min(this.files.length, this.indicator + 1);
31934 * @class Roo.bootstrap.RadioSet
31935 * @extends Roo.bootstrap.Input
31936 * Bootstrap RadioSet class
31937 * @cfg {String} indicatorpos (left|right) default left
31938 * @cfg {Boolean} inline (true|false) inline the element (default true)
31939 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
31941 * Create a new RadioSet
31942 * @param {Object} config The config object
31945 Roo.bootstrap.RadioSet = function(config){
31947 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
31951 Roo.bootstrap.RadioSet.register(this);
31956 * Fires when the element is checked or unchecked.
31957 * @param {Roo.bootstrap.RadioSet} this This radio
31958 * @param {Roo.bootstrap.Radio} item The checked item
31965 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
31975 indicatorpos : 'left',
31977 getAutoCreate : function()
31981 cls : 'roo-radio-set-label',
31985 html : this.fieldLabel
31990 if(this.indicatorpos == 'left'){
31993 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
31994 tooltip : 'This field is required'
31999 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32000 tooltip : 'This field is required'
32006 cls : 'roo-radio-set-items'
32009 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32011 if (align === 'left' && this.fieldLabel.length) {
32013 label.cls += ' col-md-' + this.labelWidth;
32016 cls : "col-md-" + (12 - this.labelWidth),
32025 cls : 'roo-radio-set',
32029 cls : 'roo-radio-set-input',
32032 value : this.value ? this.value : ''
32040 cfg.cls += ' roo-radio-set-inline';
32047 initEvents : function()
32049 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32050 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32052 if(!this.fieldLabel.length){
32053 this.labelEl.hide();
32056 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32057 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32059 this.indicatorEl().hide();
32061 this.originalValue = this.getValue();
32065 inputEl: function ()
32067 return this.el.select('.roo-radio-set-input', true).first();
32070 getChildContainer : function()
32072 return this.itemsEl;
32075 register : function(item)
32077 this.radioes.push(item);
32080 item.el.addClass('radio-inline');
32085 validate : function()
32089 Roo.each(this.radioes, function(i){
32098 if(this.disabled || this.allowBlank || valid){
32103 this.markInvalid();
32108 markValid : function()
32110 if(this.labelEl.isVisible(true)){
32111 this.indicatorEl().hide();
32114 this.el.removeClass([this.invalidClass, this.validClass]);
32115 this.el.addClass(this.validClass);
32117 this.fireEvent('valid', this);
32120 markInvalid : function(msg)
32122 if(this.allowBlank || this.disabled){
32126 if(this.labelEl.isVisible(true)){
32127 this.indicatorEl().show();
32130 this.el.removeClass([this.invalidClass, this.validClass]);
32131 this.el.addClass(this.invalidClass);
32133 this.fireEvent('invalid', this, msg);
32137 setValue : function(v, suppressEvent)
32139 Roo.each(this.radioes, function(i){
32142 i.el.removeClass('checked');
32144 if(i.value === v || i.value.toString() === v.toString()){
32146 i.el.addClass('checked');
32148 if(suppressEvent !== true){
32149 this.fireEvent('check', this, i);
32155 Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32159 clearInvalid : function(){
32161 if(!this.el || this.preventMark){
32165 if(this.labelEl.isVisible(true)){
32166 this.indicatorEl().hide();
32169 this.el.removeClass([this.invalidClass, this.validClass]);
32171 this.fireEvent('valid', this);
32176 Roo.apply(Roo.bootstrap.RadioSet, {
32180 register : function(set)
32182 this.groups[set.name] = set;
32185 get: function(name)
32187 if (typeof(this.groups[name]) == 'undefined') {
32191 return this.groups[name] ;
32197 * Ext JS Library 1.1.1
32198 * Copyright(c) 2006-2007, Ext JS, LLC.
32200 * Originally Released Under LGPL - original licence link has changed is not relivant.
32203 * <script type="text/javascript">
32208 * @class Roo.bootstrap.SplitBar
32209 * @extends Roo.util.Observable
32210 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32214 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32215 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32216 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32217 split.minSize = 100;
32218 split.maxSize = 600;
32219 split.animate = true;
32220 split.on('moved', splitterMoved);
32223 * Create a new SplitBar
32224 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32225 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32226 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32227 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32228 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32229 position of the SplitBar).
32231 Roo.bootstrap.SplitBar = function(cfg){
32236 // dragElement : elm
32237 // resizingElement: el,
32239 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32240 // placement : Roo.bootstrap.SplitBar.LEFT ,
32241 // existingProxy ???
32244 this.el = Roo.get(cfg.dragElement, true);
32245 this.el.dom.unselectable = "on";
32247 this.resizingEl = Roo.get(cfg.resizingElement, true);
32251 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32252 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32255 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32258 * The minimum size of the resizing element. (Defaults to 0)
32264 * The maximum size of the resizing element. (Defaults to 2000)
32267 this.maxSize = 2000;
32270 * Whether to animate the transition to the new size
32273 this.animate = false;
32276 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32279 this.useShim = false;
32284 if(!cfg.existingProxy){
32286 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32288 this.proxy = Roo.get(cfg.existingProxy).dom;
32291 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32294 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32297 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32300 this.dragSpecs = {};
32303 * @private The adapter to use to positon and resize elements
32305 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32306 this.adapter.init(this);
32308 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32310 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32311 this.el.addClass("roo-splitbar-h");
32314 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32315 this.el.addClass("roo-splitbar-v");
32321 * Fires when the splitter is moved (alias for {@link #event-moved})
32322 * @param {Roo.bootstrap.SplitBar} this
32323 * @param {Number} newSize the new width or height
32328 * Fires when the splitter is moved
32329 * @param {Roo.bootstrap.SplitBar} this
32330 * @param {Number} newSize the new width or height
32334 * @event beforeresize
32335 * Fires before the splitter is dragged
32336 * @param {Roo.bootstrap.SplitBar} this
32338 "beforeresize" : true,
32340 "beforeapply" : true
32343 Roo.util.Observable.call(this);
32346 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32347 onStartProxyDrag : function(x, y){
32348 this.fireEvent("beforeresize", this);
32350 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32352 o.enableDisplayMode("block");
32353 // all splitbars share the same overlay
32354 Roo.bootstrap.SplitBar.prototype.overlay = o;
32356 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32357 this.overlay.show();
32358 Roo.get(this.proxy).setDisplayed("block");
32359 var size = this.adapter.getElementSize(this);
32360 this.activeMinSize = this.getMinimumSize();;
32361 this.activeMaxSize = this.getMaximumSize();;
32362 var c1 = size - this.activeMinSize;
32363 var c2 = Math.max(this.activeMaxSize - size, 0);
32364 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32365 this.dd.resetConstraints();
32366 this.dd.setXConstraint(
32367 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32368 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32370 this.dd.setYConstraint(0, 0);
32372 this.dd.resetConstraints();
32373 this.dd.setXConstraint(0, 0);
32374 this.dd.setYConstraint(
32375 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32376 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32379 this.dragSpecs.startSize = size;
32380 this.dragSpecs.startPoint = [x, y];
32381 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32385 * @private Called after the drag operation by the DDProxy
32387 onEndProxyDrag : function(e){
32388 Roo.get(this.proxy).setDisplayed(false);
32389 var endPoint = Roo.lib.Event.getXY(e);
32391 this.overlay.hide();
32394 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32395 newSize = this.dragSpecs.startSize +
32396 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32397 endPoint[0] - this.dragSpecs.startPoint[0] :
32398 this.dragSpecs.startPoint[0] - endPoint[0]
32401 newSize = this.dragSpecs.startSize +
32402 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32403 endPoint[1] - this.dragSpecs.startPoint[1] :
32404 this.dragSpecs.startPoint[1] - endPoint[1]
32407 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32408 if(newSize != this.dragSpecs.startSize){
32409 if(this.fireEvent('beforeapply', this, newSize) !== false){
32410 this.adapter.setElementSize(this, newSize);
32411 this.fireEvent("moved", this, newSize);
32412 this.fireEvent("resize", this, newSize);
32418 * Get the adapter this SplitBar uses
32419 * @return The adapter object
32421 getAdapter : function(){
32422 return this.adapter;
32426 * Set the adapter this SplitBar uses
32427 * @param {Object} adapter A SplitBar adapter object
32429 setAdapter : function(adapter){
32430 this.adapter = adapter;
32431 this.adapter.init(this);
32435 * Gets the minimum size for the resizing element
32436 * @return {Number} The minimum size
32438 getMinimumSize : function(){
32439 return this.minSize;
32443 * Sets the minimum size for the resizing element
32444 * @param {Number} minSize The minimum size
32446 setMinimumSize : function(minSize){
32447 this.minSize = minSize;
32451 * Gets the maximum size for the resizing element
32452 * @return {Number} The maximum size
32454 getMaximumSize : function(){
32455 return this.maxSize;
32459 * Sets the maximum size for the resizing element
32460 * @param {Number} maxSize The maximum size
32462 setMaximumSize : function(maxSize){
32463 this.maxSize = maxSize;
32467 * Sets the initialize size for the resizing element
32468 * @param {Number} size The initial size
32470 setCurrentSize : function(size){
32471 var oldAnimate = this.animate;
32472 this.animate = false;
32473 this.adapter.setElementSize(this, size);
32474 this.animate = oldAnimate;
32478 * Destroy this splitbar.
32479 * @param {Boolean} removeEl True to remove the element
32481 destroy : function(removeEl){
32483 this.shim.remove();
32486 this.proxy.parentNode.removeChild(this.proxy);
32494 * @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.
32496 Roo.bootstrap.SplitBar.createProxy = function(dir){
32497 var proxy = new Roo.Element(document.createElement("div"));
32498 proxy.unselectable();
32499 var cls = 'roo-splitbar-proxy';
32500 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32501 document.body.appendChild(proxy.dom);
32506 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32507 * Default Adapter. It assumes the splitter and resizing element are not positioned
32508 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32510 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32513 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32514 // do nothing for now
32515 init : function(s){
32519 * Called before drag operations to get the current size of the resizing element.
32520 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32522 getElementSize : function(s){
32523 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32524 return s.resizingEl.getWidth();
32526 return s.resizingEl.getHeight();
32531 * Called after drag operations to set the size of the resizing element.
32532 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32533 * @param {Number} newSize The new size to set
32534 * @param {Function} onComplete A function to be invoked when resizing is complete
32536 setElementSize : function(s, newSize, onComplete){
32537 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32539 s.resizingEl.setWidth(newSize);
32541 onComplete(s, newSize);
32544 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32549 s.resizingEl.setHeight(newSize);
32551 onComplete(s, newSize);
32554 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32561 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32562 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32563 * Adapter that moves the splitter element to align with the resized sizing element.
32564 * Used with an absolute positioned SplitBar.
32565 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32566 * document.body, make sure you assign an id to the body element.
32568 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32569 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32570 this.container = Roo.get(container);
32573 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32574 init : function(s){
32575 this.basic.init(s);
32578 getElementSize : function(s){
32579 return this.basic.getElementSize(s);
32582 setElementSize : function(s, newSize, onComplete){
32583 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32586 moveSplitter : function(s){
32587 var yes = Roo.bootstrap.SplitBar;
32588 switch(s.placement){
32590 s.el.setX(s.resizingEl.getRight());
32593 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32596 s.el.setY(s.resizingEl.getBottom());
32599 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32606 * Orientation constant - Create a vertical SplitBar
32610 Roo.bootstrap.SplitBar.VERTICAL = 1;
32613 * Orientation constant - Create a horizontal SplitBar
32617 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32620 * Placement constant - The resizing element is to the left of the splitter element
32624 Roo.bootstrap.SplitBar.LEFT = 1;
32627 * Placement constant - The resizing element is to the right of the splitter element
32631 Roo.bootstrap.SplitBar.RIGHT = 2;
32634 * Placement constant - The resizing element is positioned above the splitter element
32638 Roo.bootstrap.SplitBar.TOP = 3;
32641 * Placement constant - The resizing element is positioned under splitter element
32645 Roo.bootstrap.SplitBar.BOTTOM = 4;
32646 Roo.namespace("Roo.bootstrap.layout");/*
32648 * Ext JS Library 1.1.1
32649 * Copyright(c) 2006-2007, Ext JS, LLC.
32651 * Originally Released Under LGPL - original licence link has changed is not relivant.
32654 * <script type="text/javascript">
32658 * @class Roo.bootstrap.layout.Manager
32659 * @extends Roo.bootstrap.Component
32660 * Base class for layout managers.
32662 Roo.bootstrap.layout.Manager = function(config)
32664 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32670 /** false to disable window resize monitoring @type Boolean */
32671 this.monitorWindowResize = true;
32676 * Fires when a layout is performed.
32677 * @param {Roo.LayoutManager} this
32681 * @event regionresized
32682 * Fires when the user resizes a region.
32683 * @param {Roo.LayoutRegion} region The resized region
32684 * @param {Number} newSize The new size (width for east/west, height for north/south)
32686 "regionresized" : true,
32688 * @event regioncollapsed
32689 * Fires when a region is collapsed.
32690 * @param {Roo.LayoutRegion} region The collapsed region
32692 "regioncollapsed" : true,
32694 * @event regionexpanded
32695 * Fires when a region is expanded.
32696 * @param {Roo.LayoutRegion} region The expanded region
32698 "regionexpanded" : true
32700 this.updating = false;
32703 this.el = Roo.get(config.el);
32709 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32714 monitorWindowResize : true,
32720 onRender : function(ct, position)
32723 this.el = Roo.get(ct);
32726 //this.fireEvent('render',this);
32730 initEvents: function()
32734 // ie scrollbar fix
32735 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32736 document.body.scroll = "no";
32737 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32738 this.el.position('relative');
32740 this.id = this.el.id;
32741 this.el.addClass("roo-layout-container");
32742 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32743 if(this.el.dom != document.body ) {
32744 this.el.on('resize', this.layout,this);
32745 this.el.on('show', this.layout,this);
32751 * Returns true if this layout is currently being updated
32752 * @return {Boolean}
32754 isUpdating : function(){
32755 return this.updating;
32759 * Suspend the LayoutManager from doing auto-layouts while
32760 * making multiple add or remove calls
32762 beginUpdate : function(){
32763 this.updating = true;
32767 * Restore auto-layouts and optionally disable the manager from performing a layout
32768 * @param {Boolean} noLayout true to disable a layout update
32770 endUpdate : function(noLayout){
32771 this.updating = false;
32777 layout: function(){
32781 onRegionResized : function(region, newSize){
32782 this.fireEvent("regionresized", region, newSize);
32786 onRegionCollapsed : function(region){
32787 this.fireEvent("regioncollapsed", region);
32790 onRegionExpanded : function(region){
32791 this.fireEvent("regionexpanded", region);
32795 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32796 * performs box-model adjustments.
32797 * @return {Object} The size as an object {width: (the width), height: (the height)}
32799 getViewSize : function()
32802 if(this.el.dom != document.body){
32803 size = this.el.getSize();
32805 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32807 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32808 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32813 * Returns the Element this layout is bound to.
32814 * @return {Roo.Element}
32816 getEl : function(){
32821 * Returns the specified region.
32822 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32823 * @return {Roo.LayoutRegion}
32825 getRegion : function(target){
32826 return this.regions[target.toLowerCase()];
32829 onWindowResize : function(){
32830 if(this.monitorWindowResize){
32837 * Ext JS Library 1.1.1
32838 * Copyright(c) 2006-2007, Ext JS, LLC.
32840 * Originally Released Under LGPL - original licence link has changed is not relivant.
32843 * <script type="text/javascript">
32846 * @class Roo.bootstrap.layout.Border
32847 * @extends Roo.bootstrap.layout.Manager
32848 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32849 * please see: examples/bootstrap/nested.html<br><br>
32851 <b>The container the layout is rendered into can be either the body element or any other element.
32852 If it is not the body element, the container needs to either be an absolute positioned element,
32853 or you will need to add "position:relative" to the css of the container. You will also need to specify
32854 the container size if it is not the body element.</b>
32857 * Create a new Border
32858 * @param {Object} config Configuration options
32860 Roo.bootstrap.layout.Border = function(config){
32861 config = config || {};
32862 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32866 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32867 if(config[region]){
32868 config[region].region = region;
32869 this.addRegion(config[region]);
32875 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32877 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32879 * Creates and adds a new region if it doesn't already exist.
32880 * @param {String} target The target region key (north, south, east, west or center).
32881 * @param {Object} config The regions config object
32882 * @return {BorderLayoutRegion} The new region
32884 addRegion : function(config)
32886 if(!this.regions[config.region]){
32887 var r = this.factory(config);
32888 this.bindRegion(r);
32890 return this.regions[config.region];
32894 bindRegion : function(r){
32895 this.regions[r.config.region] = r;
32897 r.on("visibilitychange", this.layout, this);
32898 r.on("paneladded", this.layout, this);
32899 r.on("panelremoved", this.layout, this);
32900 r.on("invalidated", this.layout, this);
32901 r.on("resized", this.onRegionResized, this);
32902 r.on("collapsed", this.onRegionCollapsed, this);
32903 r.on("expanded", this.onRegionExpanded, this);
32907 * Performs a layout update.
32909 layout : function()
32911 if(this.updating) {
32915 // render all the rebions if they have not been done alreayd?
32916 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32917 if(this.regions[region] && !this.regions[region].bodyEl){
32918 this.regions[region].onRender(this.el)
32922 var size = this.getViewSize();
32923 var w = size.width;
32924 var h = size.height;
32929 //var x = 0, y = 0;
32931 var rs = this.regions;
32932 var north = rs["north"];
32933 var south = rs["south"];
32934 var west = rs["west"];
32935 var east = rs["east"];
32936 var center = rs["center"];
32937 //if(this.hideOnLayout){ // not supported anymore
32938 //c.el.setStyle("display", "none");
32940 if(north && north.isVisible()){
32941 var b = north.getBox();
32942 var m = north.getMargins();
32943 b.width = w - (m.left+m.right);
32946 centerY = b.height + b.y + m.bottom;
32947 centerH -= centerY;
32948 north.updateBox(this.safeBox(b));
32950 if(south && south.isVisible()){
32951 var b = south.getBox();
32952 var m = south.getMargins();
32953 b.width = w - (m.left+m.right);
32955 var totalHeight = (b.height + m.top + m.bottom);
32956 b.y = h - totalHeight + m.top;
32957 centerH -= totalHeight;
32958 south.updateBox(this.safeBox(b));
32960 if(west && west.isVisible()){
32961 var b = west.getBox();
32962 var m = west.getMargins();
32963 b.height = centerH - (m.top+m.bottom);
32965 b.y = centerY + m.top;
32966 var totalWidth = (b.width + m.left + m.right);
32967 centerX += totalWidth;
32968 centerW -= totalWidth;
32969 west.updateBox(this.safeBox(b));
32971 if(east && east.isVisible()){
32972 var b = east.getBox();
32973 var m = east.getMargins();
32974 b.height = centerH - (m.top+m.bottom);
32975 var totalWidth = (b.width + m.left + m.right);
32976 b.x = w - totalWidth + m.left;
32977 b.y = centerY + m.top;
32978 centerW -= totalWidth;
32979 east.updateBox(this.safeBox(b));
32982 var m = center.getMargins();
32984 x: centerX + m.left,
32985 y: centerY + m.top,
32986 width: centerW - (m.left+m.right),
32987 height: centerH - (m.top+m.bottom)
32989 //if(this.hideOnLayout){
32990 //center.el.setStyle("display", "block");
32992 center.updateBox(this.safeBox(centerBox));
32995 this.fireEvent("layout", this);
32999 safeBox : function(box){
33000 box.width = Math.max(0, box.width);
33001 box.height = Math.max(0, box.height);
33006 * Adds a ContentPanel (or subclass) to this layout.
33007 * @param {String} target The target region key (north, south, east, west or center).
33008 * @param {Roo.ContentPanel} panel The panel to add
33009 * @return {Roo.ContentPanel} The added panel
33011 add : function(target, panel){
33013 target = target.toLowerCase();
33014 return this.regions[target].add(panel);
33018 * Remove a ContentPanel (or subclass) to this layout.
33019 * @param {String} target The target region key (north, south, east, west or center).
33020 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33021 * @return {Roo.ContentPanel} The removed panel
33023 remove : function(target, panel){
33024 target = target.toLowerCase();
33025 return this.regions[target].remove(panel);
33029 * Searches all regions for a panel with the specified id
33030 * @param {String} panelId
33031 * @return {Roo.ContentPanel} The panel or null if it wasn't found
33033 findPanel : function(panelId){
33034 var rs = this.regions;
33035 for(var target in rs){
33036 if(typeof rs[target] != "function"){
33037 var p = rs[target].getPanel(panelId);
33047 * Searches all regions for a panel with the specified id and activates (shows) it.
33048 * @param {String/ContentPanel} panelId The panels id or the panel itself
33049 * @return {Roo.ContentPanel} The shown panel or null
33051 showPanel : function(panelId) {
33052 var rs = this.regions;
33053 for(var target in rs){
33054 var r = rs[target];
33055 if(typeof r != "function"){
33056 if(r.hasPanel(panelId)){
33057 return r.showPanel(panelId);
33065 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33066 * @param {Roo.state.Provider} provider (optional) An alternate state provider
33069 restoreState : function(provider){
33071 provider = Roo.state.Manager;
33073 var sm = new Roo.LayoutStateManager();
33074 sm.init(this, provider);
33080 * Adds a xtype elements to the layout.
33084 xtype : 'ContentPanel',
33091 xtype : 'NestedLayoutPanel',
33097 items : [ ... list of content panels or nested layout panels.. ]
33101 * @param {Object} cfg Xtype definition of item to add.
33103 addxtype : function(cfg)
33105 // basically accepts a pannel...
33106 // can accept a layout region..!?!?
33107 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33110 // theory? children can only be panels??
33112 //if (!cfg.xtype.match(/Panel$/)) {
33117 if (typeof(cfg.region) == 'undefined') {
33118 Roo.log("Failed to add Panel, region was not set");
33122 var region = cfg.region;
33128 xitems = cfg.items;
33135 case 'Content': // ContentPanel (el, cfg)
33136 case 'Scroll': // ContentPanel (el, cfg)
33138 cfg.autoCreate = true;
33139 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33141 // var el = this.el.createChild();
33142 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33145 this.add(region, ret);
33149 case 'TreePanel': // our new panel!
33150 cfg.el = this.el.createChild();
33151 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33152 this.add(region, ret);
33157 // create a new Layout (which is a Border Layout...
33159 var clayout = cfg.layout;
33160 clayout.el = this.el.createChild();
33161 clayout.items = clayout.items || [];
33165 // replace this exitems with the clayout ones..
33166 xitems = clayout.items;
33168 // force background off if it's in center...
33169 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33170 cfg.background = false;
33172 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33175 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33176 //console.log('adding nested layout panel ' + cfg.toSource());
33177 this.add(region, ret);
33178 nb = {}; /// find first...
33183 // needs grid and region
33185 //var el = this.getRegion(region).el.createChild();
33187 *var el = this.el.createChild();
33188 // create the grid first...
33189 cfg.grid.container = el;
33190 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33193 if (region == 'center' && this.active ) {
33194 cfg.background = false;
33197 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33199 this.add(region, ret);
33201 if (cfg.background) {
33202 // render grid on panel activation (if panel background)
33203 ret.on('activate', function(gp) {
33204 if (!gp.grid.rendered) {
33205 // gp.grid.render(el);
33209 // cfg.grid.render(el);
33215 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33216 // it was the old xcomponent building that caused this before.
33217 // espeically if border is the top element in the tree.
33227 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33229 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33230 this.add(region, ret);
33234 throw "Can not add '" + cfg.xtype + "' to Border";
33240 this.beginUpdate();
33244 Roo.each(xitems, function(i) {
33245 region = nb && i.region ? i.region : false;
33247 var add = ret.addxtype(i);
33250 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33251 if (!i.background) {
33252 abn[region] = nb[region] ;
33259 // make the last non-background panel active..
33260 //if (nb) { Roo.log(abn); }
33263 for(var r in abn) {
33264 region = this.getRegion(r);
33266 // tried using nb[r], but it does not work..
33268 region.showPanel(abn[r]);
33279 factory : function(cfg)
33282 var validRegions = Roo.bootstrap.layout.Border.regions;
33284 var target = cfg.region;
33287 var r = Roo.bootstrap.layout;
33291 return new r.North(cfg);
33293 return new r.South(cfg);
33295 return new r.East(cfg);
33297 return new r.West(cfg);
33299 return new r.Center(cfg);
33301 throw 'Layout region "'+target+'" not supported.';
33308 * Ext JS Library 1.1.1
33309 * Copyright(c) 2006-2007, Ext JS, LLC.
33311 * Originally Released Under LGPL - original licence link has changed is not relivant.
33314 * <script type="text/javascript">
33318 * @class Roo.bootstrap.layout.Basic
33319 * @extends Roo.util.Observable
33320 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33321 * and does not have a titlebar, tabs or any other features. All it does is size and position
33322 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33323 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33324 * @cfg {string} region the region that it inhabits..
33325 * @cfg {bool} skipConfig skip config?
33329 Roo.bootstrap.layout.Basic = function(config){
33331 this.mgr = config.mgr;
33333 this.position = config.region;
33335 var skipConfig = config.skipConfig;
33339 * @scope Roo.BasicLayoutRegion
33343 * @event beforeremove
33344 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33345 * @param {Roo.LayoutRegion} this
33346 * @param {Roo.ContentPanel} panel The panel
33347 * @param {Object} e The cancel event object
33349 "beforeremove" : true,
33351 * @event invalidated
33352 * Fires when the layout for this region is changed.
33353 * @param {Roo.LayoutRegion} this
33355 "invalidated" : true,
33357 * @event visibilitychange
33358 * Fires when this region is shown or hidden
33359 * @param {Roo.LayoutRegion} this
33360 * @param {Boolean} visibility true or false
33362 "visibilitychange" : true,
33364 * @event paneladded
33365 * Fires when a panel is added.
33366 * @param {Roo.LayoutRegion} this
33367 * @param {Roo.ContentPanel} panel The panel
33369 "paneladded" : true,
33371 * @event panelremoved
33372 * Fires when a panel is removed.
33373 * @param {Roo.LayoutRegion} this
33374 * @param {Roo.ContentPanel} panel The panel
33376 "panelremoved" : true,
33378 * @event beforecollapse
33379 * Fires when this region before collapse.
33380 * @param {Roo.LayoutRegion} this
33382 "beforecollapse" : true,
33385 * Fires when this region is collapsed.
33386 * @param {Roo.LayoutRegion} this
33388 "collapsed" : true,
33391 * Fires when this region is expanded.
33392 * @param {Roo.LayoutRegion} this
33397 * Fires when this region is slid into view.
33398 * @param {Roo.LayoutRegion} this
33400 "slideshow" : true,
33403 * Fires when this region slides out of view.
33404 * @param {Roo.LayoutRegion} this
33406 "slidehide" : true,
33408 * @event panelactivated
33409 * Fires when a panel is activated.
33410 * @param {Roo.LayoutRegion} this
33411 * @param {Roo.ContentPanel} panel The activated panel
33413 "panelactivated" : true,
33416 * Fires when the user resizes this region.
33417 * @param {Roo.LayoutRegion} this
33418 * @param {Number} newSize The new size (width for east/west, height for north/south)
33422 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33423 this.panels = new Roo.util.MixedCollection();
33424 this.panels.getKey = this.getPanelId.createDelegate(this);
33426 this.activePanel = null;
33427 // ensure listeners are added...
33429 if (config.listeners || config.events) {
33430 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33431 listeners : config.listeners || {},
33432 events : config.events || {}
33436 if(skipConfig !== true){
33437 this.applyConfig(config);
33441 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33443 getPanelId : function(p){
33447 applyConfig : function(config){
33448 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33449 this.config = config;
33454 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33455 * the width, for horizontal (north, south) the height.
33456 * @param {Number} newSize The new width or height
33458 resizeTo : function(newSize){
33459 var el = this.el ? this.el :
33460 (this.activePanel ? this.activePanel.getEl() : null);
33462 switch(this.position){
33465 el.setWidth(newSize);
33466 this.fireEvent("resized", this, newSize);
33470 el.setHeight(newSize);
33471 this.fireEvent("resized", this, newSize);
33477 getBox : function(){
33478 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33481 getMargins : function(){
33482 return this.margins;
33485 updateBox : function(box){
33487 var el = this.activePanel.getEl();
33488 el.dom.style.left = box.x + "px";
33489 el.dom.style.top = box.y + "px";
33490 this.activePanel.setSize(box.width, box.height);
33494 * Returns the container element for this region.
33495 * @return {Roo.Element}
33497 getEl : function(){
33498 return this.activePanel;
33502 * Returns true if this region is currently visible.
33503 * @return {Boolean}
33505 isVisible : function(){
33506 return this.activePanel ? true : false;
33509 setActivePanel : function(panel){
33510 panel = this.getPanel(panel);
33511 if(this.activePanel && this.activePanel != panel){
33512 this.activePanel.setActiveState(false);
33513 this.activePanel.getEl().setLeftTop(-10000,-10000);
33515 this.activePanel = panel;
33516 panel.setActiveState(true);
33518 panel.setSize(this.box.width, this.box.height);
33520 this.fireEvent("panelactivated", this, panel);
33521 this.fireEvent("invalidated");
33525 * Show the specified panel.
33526 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33527 * @return {Roo.ContentPanel} The shown panel or null
33529 showPanel : function(panel){
33530 panel = this.getPanel(panel);
33532 this.setActivePanel(panel);
33538 * Get the active panel for this region.
33539 * @return {Roo.ContentPanel} The active panel or null
33541 getActivePanel : function(){
33542 return this.activePanel;
33546 * Add the passed ContentPanel(s)
33547 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33548 * @return {Roo.ContentPanel} The panel added (if only one was added)
33550 add : function(panel){
33551 if(arguments.length > 1){
33552 for(var i = 0, len = arguments.length; i < len; i++) {
33553 this.add(arguments[i]);
33557 if(this.hasPanel(panel)){
33558 this.showPanel(panel);
33561 var el = panel.getEl();
33562 if(el.dom.parentNode != this.mgr.el.dom){
33563 this.mgr.el.dom.appendChild(el.dom);
33565 if(panel.setRegion){
33566 panel.setRegion(this);
33568 this.panels.add(panel);
33569 el.setStyle("position", "absolute");
33570 if(!panel.background){
33571 this.setActivePanel(panel);
33572 if(this.config.initialSize && this.panels.getCount()==1){
33573 this.resizeTo(this.config.initialSize);
33576 this.fireEvent("paneladded", this, panel);
33581 * Returns true if the panel is in this region.
33582 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33583 * @return {Boolean}
33585 hasPanel : function(panel){
33586 if(typeof panel == "object"){ // must be panel obj
33587 panel = panel.getId();
33589 return this.getPanel(panel) ? true : false;
33593 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33594 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33595 * @param {Boolean} preservePanel Overrides the config preservePanel option
33596 * @return {Roo.ContentPanel} The panel that was removed
33598 remove : function(panel, preservePanel){
33599 panel = this.getPanel(panel);
33604 this.fireEvent("beforeremove", this, panel, e);
33605 if(e.cancel === true){
33608 var panelId = panel.getId();
33609 this.panels.removeKey(panelId);
33614 * Returns the panel specified or null if it's not in this region.
33615 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33616 * @return {Roo.ContentPanel}
33618 getPanel : function(id){
33619 if(typeof id == "object"){ // must be panel obj
33622 return this.panels.get(id);
33626 * Returns this regions position (north/south/east/west/center).
33629 getPosition: function(){
33630 return this.position;
33634 * Ext JS Library 1.1.1
33635 * Copyright(c) 2006-2007, Ext JS, LLC.
33637 * Originally Released Under LGPL - original licence link has changed is not relivant.
33640 * <script type="text/javascript">
33644 * @class Roo.bootstrap.layout.Region
33645 * @extends Roo.bootstrap.layout.Basic
33646 * This class represents a region in a layout manager.
33648 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33649 * @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})
33650 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33651 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33652 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33653 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33654 * @cfg {String} title The title for the region (overrides panel titles)
33655 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33656 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33657 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33658 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33659 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33660 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33661 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33662 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33663 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33664 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33666 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33667 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33668 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33669 * @cfg {Number} width For East/West panels
33670 * @cfg {Number} height For North/South panels
33671 * @cfg {Boolean} split To show the splitter
33672 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33674 * @cfg {string} cls Extra CSS classes to add to region
33676 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33677 * @cfg {string} region the region that it inhabits..
33680 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33681 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33683 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33684 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33685 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33687 Roo.bootstrap.layout.Region = function(config)
33689 this.applyConfig(config);
33691 var mgr = config.mgr;
33692 var pos = config.region;
33693 config.skipConfig = true;
33694 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33697 this.onRender(mgr.el);
33700 this.visible = true;
33701 this.collapsed = false;
33702 this.unrendered_panels = [];
33705 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33707 position: '', // set by wrapper (eg. north/south etc..)
33708 unrendered_panels : null, // unrendered panels.
33709 createBody : function(){
33710 /** This region's body element
33711 * @type Roo.Element */
33712 this.bodyEl = this.el.createChild({
33714 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33718 onRender: function(ctr, pos)
33720 var dh = Roo.DomHelper;
33721 /** This region's container element
33722 * @type Roo.Element */
33723 this.el = dh.append(ctr.dom, {
33725 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33727 /** This region's title element
33728 * @type Roo.Element */
33730 this.titleEl = dh.append(this.el.dom,
33733 unselectable: "on",
33734 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33736 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33737 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33740 this.titleEl.enableDisplayMode();
33741 /** This region's title text element
33742 * @type HTMLElement */
33743 this.titleTextEl = this.titleEl.dom.firstChild;
33744 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33746 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33747 this.closeBtn.enableDisplayMode();
33748 this.closeBtn.on("click", this.closeClicked, this);
33749 this.closeBtn.hide();
33751 this.createBody(this.config);
33752 if(this.config.hideWhenEmpty){
33754 this.on("paneladded", this.validateVisibility, this);
33755 this.on("panelremoved", this.validateVisibility, this);
33757 if(this.autoScroll){
33758 this.bodyEl.setStyle("overflow", "auto");
33760 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33762 //if(c.titlebar !== false){
33763 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33764 this.titleEl.hide();
33766 this.titleEl.show();
33767 if(this.config.title){
33768 this.titleTextEl.innerHTML = this.config.title;
33772 if(this.config.collapsed){
33773 this.collapse(true);
33775 if(this.config.hidden){
33779 if (this.unrendered_panels && this.unrendered_panels.length) {
33780 for (var i =0;i< this.unrendered_panels.length; i++) {
33781 this.add(this.unrendered_panels[i]);
33783 this.unrendered_panels = null;
33789 applyConfig : function(c)
33792 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33793 var dh = Roo.DomHelper;
33794 if(c.titlebar !== false){
33795 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33796 this.collapseBtn.on("click", this.collapse, this);
33797 this.collapseBtn.enableDisplayMode();
33799 if(c.showPin === true || this.showPin){
33800 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33801 this.stickBtn.enableDisplayMode();
33802 this.stickBtn.on("click", this.expand, this);
33803 this.stickBtn.hide();
33808 /** This region's collapsed element
33809 * @type Roo.Element */
33812 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33813 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33816 if(c.floatable !== false){
33817 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33818 this.collapsedEl.on("click", this.collapseClick, this);
33821 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33822 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33823 id: "message", unselectable: "on", style:{"float":"left"}});
33824 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33826 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33827 this.expandBtn.on("click", this.expand, this);
33831 if(this.collapseBtn){
33832 this.collapseBtn.setVisible(c.collapsible == true);
33835 this.cmargins = c.cmargins || this.cmargins ||
33836 (this.position == "west" || this.position == "east" ?
33837 {top: 0, left: 2, right:2, bottom: 0} :
33838 {top: 2, left: 0, right:0, bottom: 2});
33840 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33843 this.bottomTabs = c.tabPosition != "top";
33845 this.autoScroll = c.autoScroll || false;
33850 this.duration = c.duration || .30;
33851 this.slideDuration = c.slideDuration || .45;
33856 * Returns true if this region is currently visible.
33857 * @return {Boolean}
33859 isVisible : function(){
33860 return this.visible;
33864 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33865 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33867 //setCollapsedTitle : function(title){
33868 // title = title || " ";
33869 // if(this.collapsedTitleTextEl){
33870 // this.collapsedTitleTextEl.innerHTML = title;
33874 getBox : function(){
33876 // if(!this.collapsed){
33877 b = this.el.getBox(false, true);
33879 // b = this.collapsedEl.getBox(false, true);
33884 getMargins : function(){
33885 return this.margins;
33886 //return this.collapsed ? this.cmargins : this.margins;
33889 highlight : function(){
33890 this.el.addClass("x-layout-panel-dragover");
33893 unhighlight : function(){
33894 this.el.removeClass("x-layout-panel-dragover");
33897 updateBox : function(box)
33899 if (!this.bodyEl) {
33900 return; // not rendered yet..
33904 if(!this.collapsed){
33905 this.el.dom.style.left = box.x + "px";
33906 this.el.dom.style.top = box.y + "px";
33907 this.updateBody(box.width, box.height);
33909 this.collapsedEl.dom.style.left = box.x + "px";
33910 this.collapsedEl.dom.style.top = box.y + "px";
33911 this.collapsedEl.setSize(box.width, box.height);
33914 this.tabs.autoSizeTabs();
33918 updateBody : function(w, h)
33921 this.el.setWidth(w);
33922 w -= this.el.getBorderWidth("rl");
33923 if(this.config.adjustments){
33924 w += this.config.adjustments[0];
33927 if(h !== null && h > 0){
33928 this.el.setHeight(h);
33929 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33930 h -= this.el.getBorderWidth("tb");
33931 if(this.config.adjustments){
33932 h += this.config.adjustments[1];
33934 this.bodyEl.setHeight(h);
33936 h = this.tabs.syncHeight(h);
33939 if(this.panelSize){
33940 w = w !== null ? w : this.panelSize.width;
33941 h = h !== null ? h : this.panelSize.height;
33943 if(this.activePanel){
33944 var el = this.activePanel.getEl();
33945 w = w !== null ? w : el.getWidth();
33946 h = h !== null ? h : el.getHeight();
33947 this.panelSize = {width: w, height: h};
33948 this.activePanel.setSize(w, h);
33950 if(Roo.isIE && this.tabs){
33951 this.tabs.el.repaint();
33956 * Returns the container element for this region.
33957 * @return {Roo.Element}
33959 getEl : function(){
33964 * Hides this region.
33967 //if(!this.collapsed){
33968 this.el.dom.style.left = "-2000px";
33971 // this.collapsedEl.dom.style.left = "-2000px";
33972 // this.collapsedEl.hide();
33974 this.visible = false;
33975 this.fireEvent("visibilitychange", this, false);
33979 * Shows this region if it was previously hidden.
33982 //if(!this.collapsed){
33985 // this.collapsedEl.show();
33987 this.visible = true;
33988 this.fireEvent("visibilitychange", this, true);
33991 closeClicked : function(){
33992 if(this.activePanel){
33993 this.remove(this.activePanel);
33997 collapseClick : function(e){
33999 e.stopPropagation();
34002 e.stopPropagation();
34008 * Collapses this region.
34009 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34012 collapse : function(skipAnim, skipCheck = false){
34013 if(this.collapsed) {
34017 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34019 this.collapsed = true;
34021 this.split.el.hide();
34023 if(this.config.animate && skipAnim !== true){
34024 this.fireEvent("invalidated", this);
34025 this.animateCollapse();
34027 this.el.setLocation(-20000,-20000);
34029 this.collapsedEl.show();
34030 this.fireEvent("collapsed", this);
34031 this.fireEvent("invalidated", this);
34037 animateCollapse : function(){
34042 * Expands this region if it was previously collapsed.
34043 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34044 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34047 expand : function(e, skipAnim){
34049 e.stopPropagation();
34051 if(!this.collapsed || this.el.hasActiveFx()) {
34055 this.afterSlideIn();
34058 this.collapsed = false;
34059 if(this.config.animate && skipAnim !== true){
34060 this.animateExpand();
34064 this.split.el.show();
34066 this.collapsedEl.setLocation(-2000,-2000);
34067 this.collapsedEl.hide();
34068 this.fireEvent("invalidated", this);
34069 this.fireEvent("expanded", this);
34073 animateExpand : function(){
34077 initTabs : function()
34079 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34081 var ts = new Roo.bootstrap.panel.Tabs({
34082 el: this.bodyEl.dom,
34083 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34084 disableTooltips: this.config.disableTabTips,
34085 toolbar : this.config.toolbar
34088 if(this.config.hideTabs){
34089 ts.stripWrap.setDisplayed(false);
34092 ts.resizeTabs = this.config.resizeTabs === true;
34093 ts.minTabWidth = this.config.minTabWidth || 40;
34094 ts.maxTabWidth = this.config.maxTabWidth || 250;
34095 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34096 ts.monitorResize = false;
34097 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34098 ts.bodyEl.addClass('roo-layout-tabs-body');
34099 this.panels.each(this.initPanelAsTab, this);
34102 initPanelAsTab : function(panel){
34103 var ti = this.tabs.addTab(
34107 this.config.closeOnTab && panel.isClosable(),
34110 if(panel.tabTip !== undefined){
34111 ti.setTooltip(panel.tabTip);
34113 ti.on("activate", function(){
34114 this.setActivePanel(panel);
34117 if(this.config.closeOnTab){
34118 ti.on("beforeclose", function(t, e){
34120 this.remove(panel);
34124 panel.tabItem = ti;
34129 updatePanelTitle : function(panel, title)
34131 if(this.activePanel == panel){
34132 this.updateTitle(title);
34135 var ti = this.tabs.getTab(panel.getEl().id);
34137 if(panel.tabTip !== undefined){
34138 ti.setTooltip(panel.tabTip);
34143 updateTitle : function(title){
34144 if(this.titleTextEl && !this.config.title){
34145 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
34149 setActivePanel : function(panel)
34151 panel = this.getPanel(panel);
34152 if(this.activePanel && this.activePanel != panel){
34153 this.activePanel.setActiveState(false);
34155 this.activePanel = panel;
34156 panel.setActiveState(true);
34157 if(this.panelSize){
34158 panel.setSize(this.panelSize.width, this.panelSize.height);
34161 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34163 this.updateTitle(panel.getTitle());
34165 this.fireEvent("invalidated", this);
34167 this.fireEvent("panelactivated", this, panel);
34171 * Shows the specified panel.
34172 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34173 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34175 showPanel : function(panel)
34177 panel = this.getPanel(panel);
34180 var tab = this.tabs.getTab(panel.getEl().id);
34181 if(tab.isHidden()){
34182 this.tabs.unhideTab(tab.id);
34186 this.setActivePanel(panel);
34193 * Get the active panel for this region.
34194 * @return {Roo.ContentPanel} The active panel or null
34196 getActivePanel : function(){
34197 return this.activePanel;
34200 validateVisibility : function(){
34201 if(this.panels.getCount() < 1){
34202 this.updateTitle(" ");
34203 this.closeBtn.hide();
34206 if(!this.isVisible()){
34213 * Adds the passed ContentPanel(s) to this region.
34214 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34215 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34217 add : function(panel)
34219 if(arguments.length > 1){
34220 for(var i = 0, len = arguments.length; i < len; i++) {
34221 this.add(arguments[i]);
34226 // if we have not been rendered yet, then we can not really do much of this..
34227 if (!this.bodyEl) {
34228 this.unrendered_panels.push(panel);
34235 if(this.hasPanel(panel)){
34236 this.showPanel(panel);
34239 panel.setRegion(this);
34240 this.panels.add(panel);
34241 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34242 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34243 // and hide them... ???
34244 this.bodyEl.dom.appendChild(panel.getEl().dom);
34245 if(panel.background !== true){
34246 this.setActivePanel(panel);
34248 this.fireEvent("paneladded", this, panel);
34255 this.initPanelAsTab(panel);
34259 if(panel.background !== true){
34260 this.tabs.activate(panel.getEl().id);
34262 this.fireEvent("paneladded", this, panel);
34267 * Hides the tab for the specified panel.
34268 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34270 hidePanel : function(panel){
34271 if(this.tabs && (panel = this.getPanel(panel))){
34272 this.tabs.hideTab(panel.getEl().id);
34277 * Unhides the tab for a previously hidden panel.
34278 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34280 unhidePanel : function(panel){
34281 if(this.tabs && (panel = this.getPanel(panel))){
34282 this.tabs.unhideTab(panel.getEl().id);
34286 clearPanels : function(){
34287 while(this.panels.getCount() > 0){
34288 this.remove(this.panels.first());
34293 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34294 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34295 * @param {Boolean} preservePanel Overrides the config preservePanel option
34296 * @return {Roo.ContentPanel} The panel that was removed
34298 remove : function(panel, preservePanel)
34300 panel = this.getPanel(panel);
34305 this.fireEvent("beforeremove", this, panel, e);
34306 if(e.cancel === true){
34309 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34310 var panelId = panel.getId();
34311 this.panels.removeKey(panelId);
34313 document.body.appendChild(panel.getEl().dom);
34316 this.tabs.removeTab(panel.getEl().id);
34317 }else if (!preservePanel){
34318 this.bodyEl.dom.removeChild(panel.getEl().dom);
34320 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34321 var p = this.panels.first();
34322 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34323 tempEl.appendChild(p.getEl().dom);
34324 this.bodyEl.update("");
34325 this.bodyEl.dom.appendChild(p.getEl().dom);
34327 this.updateTitle(p.getTitle());
34329 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34330 this.setActivePanel(p);
34332 panel.setRegion(null);
34333 if(this.activePanel == panel){
34334 this.activePanel = null;
34336 if(this.config.autoDestroy !== false && preservePanel !== true){
34337 try{panel.destroy();}catch(e){}
34339 this.fireEvent("panelremoved", this, panel);
34344 * Returns the TabPanel component used by this region
34345 * @return {Roo.TabPanel}
34347 getTabs : function(){
34351 createTool : function(parentEl, className){
34352 var btn = Roo.DomHelper.append(parentEl, {
34354 cls: "x-layout-tools-button",
34357 cls: "roo-layout-tools-button-inner " + className,
34361 btn.addClassOnOver("roo-layout-tools-button-over");
34366 * Ext JS Library 1.1.1
34367 * Copyright(c) 2006-2007, Ext JS, LLC.
34369 * Originally Released Under LGPL - original licence link has changed is not relivant.
34372 * <script type="text/javascript">
34378 * @class Roo.SplitLayoutRegion
34379 * @extends Roo.LayoutRegion
34380 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34382 Roo.bootstrap.layout.Split = function(config){
34383 this.cursor = config.cursor;
34384 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34387 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34389 splitTip : "Drag to resize.",
34390 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34391 useSplitTips : false,
34393 applyConfig : function(config){
34394 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34397 onRender : function(ctr,pos) {
34399 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34400 if(!this.config.split){
34405 var splitEl = Roo.DomHelper.append(ctr.dom, {
34407 id: this.el.id + "-split",
34408 cls: "roo-layout-split roo-layout-split-"+this.position,
34411 /** The SplitBar for this region
34412 * @type Roo.SplitBar */
34413 // does not exist yet...
34414 Roo.log([this.position, this.orientation]);
34416 this.split = new Roo.bootstrap.SplitBar({
34417 dragElement : splitEl,
34418 resizingElement: this.el,
34419 orientation : this.orientation
34422 this.split.on("moved", this.onSplitMove, this);
34423 this.split.useShim = this.config.useShim === true;
34424 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34425 if(this.useSplitTips){
34426 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34428 //if(config.collapsible){
34429 // this.split.el.on("dblclick", this.collapse, this);
34432 if(typeof this.config.minSize != "undefined"){
34433 this.split.minSize = this.config.minSize;
34435 if(typeof this.config.maxSize != "undefined"){
34436 this.split.maxSize = this.config.maxSize;
34438 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34439 this.hideSplitter();
34444 getHMaxSize : function(){
34445 var cmax = this.config.maxSize || 10000;
34446 var center = this.mgr.getRegion("center");
34447 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34450 getVMaxSize : function(){
34451 var cmax = this.config.maxSize || 10000;
34452 var center = this.mgr.getRegion("center");
34453 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34456 onSplitMove : function(split, newSize){
34457 this.fireEvent("resized", this, newSize);
34461 * Returns the {@link Roo.SplitBar} for this region.
34462 * @return {Roo.SplitBar}
34464 getSplitBar : function(){
34469 this.hideSplitter();
34470 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34473 hideSplitter : function(){
34475 this.split.el.setLocation(-2000,-2000);
34476 this.split.el.hide();
34482 this.split.el.show();
34484 Roo.bootstrap.layout.Split.superclass.show.call(this);
34487 beforeSlide: function(){
34488 if(Roo.isGecko){// firefox overflow auto bug workaround
34489 this.bodyEl.clip();
34491 this.tabs.bodyEl.clip();
34493 if(this.activePanel){
34494 this.activePanel.getEl().clip();
34496 if(this.activePanel.beforeSlide){
34497 this.activePanel.beforeSlide();
34503 afterSlide : function(){
34504 if(Roo.isGecko){// firefox overflow auto bug workaround
34505 this.bodyEl.unclip();
34507 this.tabs.bodyEl.unclip();
34509 if(this.activePanel){
34510 this.activePanel.getEl().unclip();
34511 if(this.activePanel.afterSlide){
34512 this.activePanel.afterSlide();
34518 initAutoHide : function(){
34519 if(this.autoHide !== false){
34520 if(!this.autoHideHd){
34521 var st = new Roo.util.DelayedTask(this.slideIn, this);
34522 this.autoHideHd = {
34523 "mouseout": function(e){
34524 if(!e.within(this.el, true)){
34528 "mouseover" : function(e){
34534 this.el.on(this.autoHideHd);
34538 clearAutoHide : function(){
34539 if(this.autoHide !== false){
34540 this.el.un("mouseout", this.autoHideHd.mouseout);
34541 this.el.un("mouseover", this.autoHideHd.mouseover);
34545 clearMonitor : function(){
34546 Roo.get(document).un("click", this.slideInIf, this);
34549 // these names are backwards but not changed for compat
34550 slideOut : function(){
34551 if(this.isSlid || this.el.hasActiveFx()){
34554 this.isSlid = true;
34555 if(this.collapseBtn){
34556 this.collapseBtn.hide();
34558 this.closeBtnState = this.closeBtn.getStyle('display');
34559 this.closeBtn.hide();
34561 this.stickBtn.show();
34564 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34565 this.beforeSlide();
34566 this.el.setStyle("z-index", 10001);
34567 this.el.slideIn(this.getSlideAnchor(), {
34568 callback: function(){
34570 this.initAutoHide();
34571 Roo.get(document).on("click", this.slideInIf, this);
34572 this.fireEvent("slideshow", this);
34579 afterSlideIn : function(){
34580 this.clearAutoHide();
34581 this.isSlid = false;
34582 this.clearMonitor();
34583 this.el.setStyle("z-index", "");
34584 if(this.collapseBtn){
34585 this.collapseBtn.show();
34587 this.closeBtn.setStyle('display', this.closeBtnState);
34589 this.stickBtn.hide();
34591 this.fireEvent("slidehide", this);
34594 slideIn : function(cb){
34595 if(!this.isSlid || this.el.hasActiveFx()){
34599 this.isSlid = false;
34600 this.beforeSlide();
34601 this.el.slideOut(this.getSlideAnchor(), {
34602 callback: function(){
34603 this.el.setLeftTop(-10000, -10000);
34605 this.afterSlideIn();
34613 slideInIf : function(e){
34614 if(!e.within(this.el)){
34619 animateCollapse : function(){
34620 this.beforeSlide();
34621 this.el.setStyle("z-index", 20000);
34622 var anchor = this.getSlideAnchor();
34623 this.el.slideOut(anchor, {
34624 callback : function(){
34625 this.el.setStyle("z-index", "");
34626 this.collapsedEl.slideIn(anchor, {duration:.3});
34628 this.el.setLocation(-10000,-10000);
34630 this.fireEvent("collapsed", this);
34637 animateExpand : function(){
34638 this.beforeSlide();
34639 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34640 this.el.setStyle("z-index", 20000);
34641 this.collapsedEl.hide({
34644 this.el.slideIn(this.getSlideAnchor(), {
34645 callback : function(){
34646 this.el.setStyle("z-index", "");
34649 this.split.el.show();
34651 this.fireEvent("invalidated", this);
34652 this.fireEvent("expanded", this);
34680 getAnchor : function(){
34681 return this.anchors[this.position];
34684 getCollapseAnchor : function(){
34685 return this.canchors[this.position];
34688 getSlideAnchor : function(){
34689 return this.sanchors[this.position];
34692 getAlignAdj : function(){
34693 var cm = this.cmargins;
34694 switch(this.position){
34710 getExpandAdj : function(){
34711 var c = this.collapsedEl, cm = this.cmargins;
34712 switch(this.position){
34714 return [-(cm.right+c.getWidth()+cm.left), 0];
34717 return [cm.right+c.getWidth()+cm.left, 0];
34720 return [0, -(cm.top+cm.bottom+c.getHeight())];
34723 return [0, cm.top+cm.bottom+c.getHeight()];
34729 * Ext JS Library 1.1.1
34730 * Copyright(c) 2006-2007, Ext JS, LLC.
34732 * Originally Released Under LGPL - original licence link has changed is not relivant.
34735 * <script type="text/javascript">
34738 * These classes are private internal classes
34740 Roo.bootstrap.layout.Center = function(config){
34741 config.region = "center";
34742 Roo.bootstrap.layout.Region.call(this, config);
34743 this.visible = true;
34744 this.minWidth = config.minWidth || 20;
34745 this.minHeight = config.minHeight || 20;
34748 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34750 // center panel can't be hidden
34754 // center panel can't be hidden
34757 getMinWidth: function(){
34758 return this.minWidth;
34761 getMinHeight: function(){
34762 return this.minHeight;
34775 Roo.bootstrap.layout.North = function(config)
34777 config.region = 'north';
34778 config.cursor = 'n-resize';
34780 Roo.bootstrap.layout.Split.call(this, config);
34784 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34785 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34786 this.split.el.addClass("roo-layout-split-v");
34788 var size = config.initialSize || config.height;
34789 if(typeof size != "undefined"){
34790 this.el.setHeight(size);
34793 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34795 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34799 getBox : function(){
34800 if(this.collapsed){
34801 return this.collapsedEl.getBox();
34803 var box = this.el.getBox();
34805 box.height += this.split.el.getHeight();
34810 updateBox : function(box){
34811 if(this.split && !this.collapsed){
34812 box.height -= this.split.el.getHeight();
34813 this.split.el.setLeft(box.x);
34814 this.split.el.setTop(box.y+box.height);
34815 this.split.el.setWidth(box.width);
34817 if(this.collapsed){
34818 this.updateBody(box.width, null);
34820 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34828 Roo.bootstrap.layout.South = function(config){
34829 config.region = 'south';
34830 config.cursor = 's-resize';
34831 Roo.bootstrap.layout.Split.call(this, config);
34833 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34834 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34835 this.split.el.addClass("roo-layout-split-v");
34837 var size = config.initialSize || config.height;
34838 if(typeof size != "undefined"){
34839 this.el.setHeight(size);
34843 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34844 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34845 getBox : function(){
34846 if(this.collapsed){
34847 return this.collapsedEl.getBox();
34849 var box = this.el.getBox();
34851 var sh = this.split.el.getHeight();
34858 updateBox : function(box){
34859 if(this.split && !this.collapsed){
34860 var sh = this.split.el.getHeight();
34863 this.split.el.setLeft(box.x);
34864 this.split.el.setTop(box.y-sh);
34865 this.split.el.setWidth(box.width);
34867 if(this.collapsed){
34868 this.updateBody(box.width, null);
34870 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34874 Roo.bootstrap.layout.East = function(config){
34875 config.region = "east";
34876 config.cursor = "e-resize";
34877 Roo.bootstrap.layout.Split.call(this, config);
34879 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34880 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34881 this.split.el.addClass("roo-layout-split-h");
34883 var size = config.initialSize || config.width;
34884 if(typeof size != "undefined"){
34885 this.el.setWidth(size);
34888 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34889 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34890 getBox : function(){
34891 if(this.collapsed){
34892 return this.collapsedEl.getBox();
34894 var box = this.el.getBox();
34896 var sw = this.split.el.getWidth();
34903 updateBox : function(box){
34904 if(this.split && !this.collapsed){
34905 var sw = this.split.el.getWidth();
34907 this.split.el.setLeft(box.x);
34908 this.split.el.setTop(box.y);
34909 this.split.el.setHeight(box.height);
34912 if(this.collapsed){
34913 this.updateBody(null, box.height);
34915 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34919 Roo.bootstrap.layout.West = function(config){
34920 config.region = "west";
34921 config.cursor = "w-resize";
34923 Roo.bootstrap.layout.Split.call(this, config);
34925 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34926 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34927 this.split.el.addClass("roo-layout-split-h");
34931 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34932 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34934 onRender: function(ctr, pos)
34936 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34937 var size = this.config.initialSize || this.config.width;
34938 if(typeof size != "undefined"){
34939 this.el.setWidth(size);
34943 getBox : function(){
34944 if(this.collapsed){
34945 return this.collapsedEl.getBox();
34947 var box = this.el.getBox();
34949 box.width += this.split.el.getWidth();
34954 updateBox : function(box){
34955 if(this.split && !this.collapsed){
34956 var sw = this.split.el.getWidth();
34958 this.split.el.setLeft(box.x+box.width);
34959 this.split.el.setTop(box.y);
34960 this.split.el.setHeight(box.height);
34962 if(this.collapsed){
34963 this.updateBody(null, box.height);
34965 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34968 Roo.namespace("Roo.bootstrap.panel");/*
34970 * Ext JS Library 1.1.1
34971 * Copyright(c) 2006-2007, Ext JS, LLC.
34973 * Originally Released Under LGPL - original licence link has changed is not relivant.
34976 * <script type="text/javascript">
34979 * @class Roo.ContentPanel
34980 * @extends Roo.util.Observable
34981 * A basic ContentPanel element.
34982 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34983 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34984 * @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
34985 * @cfg {Boolean} closable True if the panel can be closed/removed
34986 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34987 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34988 * @cfg {Toolbar} toolbar A toolbar for this panel
34989 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34990 * @cfg {String} title The title for this panel
34991 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34992 * @cfg {String} url Calls {@link #setUrl} with this value
34993 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34994 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34995 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34996 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34997 * @cfg {Boolean} badges render the badges
35000 * Create a new ContentPanel.
35001 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35002 * @param {String/Object} config A string to set only the title or a config object
35003 * @param {String} content (optional) Set the HTML content for this panel
35004 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35006 Roo.bootstrap.panel.Content = function( config){
35008 this.tpl = config.tpl || false;
35010 var el = config.el;
35011 var content = config.content;
35013 if(config.autoCreate){ // xtype is available if this is called from factory
35016 this.el = Roo.get(el);
35017 if(!this.el && config && config.autoCreate){
35018 if(typeof config.autoCreate == "object"){
35019 if(!config.autoCreate.id){
35020 config.autoCreate.id = config.id||el;
35022 this.el = Roo.DomHelper.append(document.body,
35023 config.autoCreate, true);
35025 var elcfg = { tag: "div",
35026 cls: "roo-layout-inactive-content",
35030 elcfg.html = config.html;
35034 this.el = Roo.DomHelper.append(document.body, elcfg , true);
35037 this.closable = false;
35038 this.loaded = false;
35039 this.active = false;
35042 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35044 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35046 this.wrapEl = this.el; //this.el.wrap();
35048 if (config.toolbar.items) {
35049 ti = config.toolbar.items ;
35050 delete config.toolbar.items ;
35054 this.toolbar.render(this.wrapEl, 'before');
35055 for(var i =0;i < ti.length;i++) {
35056 // Roo.log(['add child', items[i]]);
35057 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35059 this.toolbar.items = nitems;
35060 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35061 delete config.toolbar;
35065 // xtype created footer. - not sure if will work as we normally have to render first..
35066 if (this.footer && !this.footer.el && this.footer.xtype) {
35067 if (!this.wrapEl) {
35068 this.wrapEl = this.el.wrap();
35071 this.footer.container = this.wrapEl.createChild();
35073 this.footer = Roo.factory(this.footer, Roo);
35078 if(typeof config == "string"){
35079 this.title = config;
35081 Roo.apply(this, config);
35085 this.resizeEl = Roo.get(this.resizeEl, true);
35087 this.resizeEl = this.el;
35089 // handle view.xtype
35097 * Fires when this panel is activated.
35098 * @param {Roo.ContentPanel} this
35102 * @event deactivate
35103 * Fires when this panel is activated.
35104 * @param {Roo.ContentPanel} this
35106 "deactivate" : true,
35110 * Fires when this panel is resized if fitToFrame is true.
35111 * @param {Roo.ContentPanel} this
35112 * @param {Number} width The width after any component adjustments
35113 * @param {Number} height The height after any component adjustments
35119 * Fires when this tab is created
35120 * @param {Roo.ContentPanel} this
35131 if(this.autoScroll){
35132 this.resizeEl.setStyle("overflow", "auto");
35134 // fix randome scrolling
35135 //this.el.on('scroll', function() {
35136 // Roo.log('fix random scolling');
35137 // this.scrollTo('top',0);
35140 content = content || this.content;
35142 this.setContent(content);
35144 if(config && config.url){
35145 this.setUrl(this.url, this.params, this.loadOnce);
35150 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35152 if (this.view && typeof(this.view.xtype) != 'undefined') {
35153 this.view.el = this.el.appendChild(document.createElement("div"));
35154 this.view = Roo.factory(this.view);
35155 this.view.render && this.view.render(false, '');
35159 this.fireEvent('render', this);
35162 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35166 setRegion : function(region){
35167 this.region = region;
35168 this.setActiveClass(region && !this.background);
35172 setActiveClass: function(state)
35175 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35176 this.el.setStyle('position','relative');
35178 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35179 this.el.setStyle('position', 'absolute');
35184 * Returns the toolbar for this Panel if one was configured.
35185 * @return {Roo.Toolbar}
35187 getToolbar : function(){
35188 return this.toolbar;
35191 setActiveState : function(active)
35193 this.active = active;
35194 this.setActiveClass(active);
35196 this.fireEvent("deactivate", this);
35198 this.fireEvent("activate", this);
35202 * Updates this panel's element
35203 * @param {String} content The new content
35204 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35206 setContent : function(content, loadScripts){
35207 this.el.update(content, loadScripts);
35210 ignoreResize : function(w, h){
35211 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35214 this.lastSize = {width: w, height: h};
35219 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35220 * @return {Roo.UpdateManager} The UpdateManager
35222 getUpdateManager : function(){
35223 return this.el.getUpdateManager();
35226 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35227 * @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:
35230 url: "your-url.php",
35231 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35232 callback: yourFunction,
35233 scope: yourObject, //(optional scope)
35236 text: "Loading...",
35241 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35242 * 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.
35243 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
35244 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35245 * @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.
35246 * @return {Roo.ContentPanel} this
35249 var um = this.el.getUpdateManager();
35250 um.update.apply(um, arguments);
35256 * 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.
35257 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35258 * @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)
35259 * @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)
35260 * @return {Roo.UpdateManager} The UpdateManager
35262 setUrl : function(url, params, loadOnce){
35263 if(this.refreshDelegate){
35264 this.removeListener("activate", this.refreshDelegate);
35266 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35267 this.on("activate", this.refreshDelegate);
35268 return this.el.getUpdateManager();
35271 _handleRefresh : function(url, params, loadOnce){
35272 if(!loadOnce || !this.loaded){
35273 var updater = this.el.getUpdateManager();
35274 updater.update(url, params, this._setLoaded.createDelegate(this));
35278 _setLoaded : function(){
35279 this.loaded = true;
35283 * Returns this panel's id
35286 getId : function(){
35291 * Returns this panel's element - used by regiosn to add.
35292 * @return {Roo.Element}
35294 getEl : function(){
35295 return this.wrapEl || this.el;
35300 adjustForComponents : function(width, height)
35302 //Roo.log('adjustForComponents ');
35303 if(this.resizeEl != this.el){
35304 width -= this.el.getFrameWidth('lr');
35305 height -= this.el.getFrameWidth('tb');
35308 var te = this.toolbar.getEl();
35309 height -= te.getHeight();
35310 te.setWidth(width);
35313 var te = this.footer.getEl();
35314 Roo.log("footer:" + te.getHeight());
35316 height -= te.getHeight();
35317 te.setWidth(width);
35321 if(this.adjustments){
35322 width += this.adjustments[0];
35323 height += this.adjustments[1];
35325 return {"width": width, "height": height};
35328 setSize : function(width, height){
35329 if(this.fitToFrame && !this.ignoreResize(width, height)){
35330 if(this.fitContainer && this.resizeEl != this.el){
35331 this.el.setSize(width, height);
35333 var size = this.adjustForComponents(width, height);
35334 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35335 this.fireEvent('resize', this, size.width, size.height);
35340 * Returns this panel's title
35343 getTitle : function(){
35348 * Set this panel's title
35349 * @param {String} title
35351 setTitle : function(title){
35352 this.title = title;
35354 this.region.updatePanelTitle(this, title);
35359 * Returns true is this panel was configured to be closable
35360 * @return {Boolean}
35362 isClosable : function(){
35363 return this.closable;
35366 beforeSlide : function(){
35368 this.resizeEl.clip();
35371 afterSlide : function(){
35373 this.resizeEl.unclip();
35377 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35378 * Will fail silently if the {@link #setUrl} method has not been called.
35379 * This does not activate the panel, just updates its content.
35381 refresh : function(){
35382 if(this.refreshDelegate){
35383 this.loaded = false;
35384 this.refreshDelegate();
35389 * Destroys this panel
35391 destroy : function(){
35392 this.el.removeAllListeners();
35393 var tempEl = document.createElement("span");
35394 tempEl.appendChild(this.el.dom);
35395 tempEl.innerHTML = "";
35401 * form - if the content panel contains a form - this is a reference to it.
35402 * @type {Roo.form.Form}
35406 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35407 * This contains a reference to it.
35413 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35423 * @param {Object} cfg Xtype definition of item to add.
35427 getChildContainer: function () {
35428 return this.getEl();
35433 var ret = new Roo.factory(cfg);
35438 if (cfg.xtype.match(/^Form$/)) {
35441 //if (this.footer) {
35442 // el = this.footer.container.insertSibling(false, 'before');
35444 el = this.el.createChild();
35447 this.form = new Roo.form.Form(cfg);
35450 if ( this.form.allItems.length) {
35451 this.form.render(el.dom);
35455 // should only have one of theses..
35456 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35457 // views.. should not be just added - used named prop 'view''
35459 cfg.el = this.el.appendChild(document.createElement("div"));
35462 var ret = new Roo.factory(cfg);
35464 ret.render && ret.render(false, ''); // render blank..
35474 * @class Roo.bootstrap.panel.Grid
35475 * @extends Roo.bootstrap.panel.Content
35477 * Create a new GridPanel.
35478 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35479 * @param {Object} config A the config object
35485 Roo.bootstrap.panel.Grid = function(config)
35489 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35490 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35492 config.el = this.wrapper;
35493 //this.el = this.wrapper;
35495 if (config.container) {
35496 // ctor'ed from a Border/panel.grid
35499 this.wrapper.setStyle("overflow", "hidden");
35500 this.wrapper.addClass('roo-grid-container');
35505 if(config.toolbar){
35506 var tool_el = this.wrapper.createChild();
35507 this.toolbar = Roo.factory(config.toolbar);
35509 if (config.toolbar.items) {
35510 ti = config.toolbar.items ;
35511 delete config.toolbar.items ;
35515 this.toolbar.render(tool_el);
35516 for(var i =0;i < ti.length;i++) {
35517 // Roo.log(['add child', items[i]]);
35518 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35520 this.toolbar.items = nitems;
35522 delete config.toolbar;
35525 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35526 config.grid.scrollBody = true;;
35527 config.grid.monitorWindowResize = false; // turn off autosizing
35528 config.grid.autoHeight = false;
35529 config.grid.autoWidth = false;
35531 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35533 if (config.background) {
35534 // render grid on panel activation (if panel background)
35535 this.on('activate', function(gp) {
35536 if (!gp.grid.rendered) {
35537 gp.grid.render(this.wrapper);
35538 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35543 this.grid.render(this.wrapper);
35544 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35547 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35548 // ??? needed ??? config.el = this.wrapper;
35553 // xtype created footer. - not sure if will work as we normally have to render first..
35554 if (this.footer && !this.footer.el && this.footer.xtype) {
35556 var ctr = this.grid.getView().getFooterPanel(true);
35557 this.footer.dataSource = this.grid.dataSource;
35558 this.footer = Roo.factory(this.footer, Roo);
35559 this.footer.render(ctr);
35569 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35570 getId : function(){
35571 return this.grid.id;
35575 * Returns the grid for this panel
35576 * @return {Roo.bootstrap.Table}
35578 getGrid : function(){
35582 setSize : function(width, height){
35583 if(!this.ignoreResize(width, height)){
35584 var grid = this.grid;
35585 var size = this.adjustForComponents(width, height);
35586 var gridel = grid.getGridEl();
35587 gridel.setSize(size.width, size.height);
35589 var thd = grid.getGridEl().select('thead',true).first();
35590 var tbd = grid.getGridEl().select('tbody', true).first();
35592 tbd.setSize(width, height - thd.getHeight());
35601 beforeSlide : function(){
35602 this.grid.getView().scroller.clip();
35605 afterSlide : function(){
35606 this.grid.getView().scroller.unclip();
35609 destroy : function(){
35610 this.grid.destroy();
35612 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35617 * @class Roo.bootstrap.panel.Nest
35618 * @extends Roo.bootstrap.panel.Content
35620 * Create a new Panel, that can contain a layout.Border.
35623 * @param {Roo.BorderLayout} layout The layout for this panel
35624 * @param {String/Object} config A string to set only the title or a config object
35626 Roo.bootstrap.panel.Nest = function(config)
35628 // construct with only one argument..
35629 /* FIXME - implement nicer consturctors
35630 if (layout.layout) {
35632 layout = config.layout;
35633 delete config.layout;
35635 if (layout.xtype && !layout.getEl) {
35636 // then layout needs constructing..
35637 layout = Roo.factory(layout, Roo);
35641 config.el = config.layout.getEl();
35643 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35645 config.layout.monitorWindowResize = false; // turn off autosizing
35646 this.layout = config.layout;
35647 this.layout.getEl().addClass("roo-layout-nested-layout");
35654 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35656 setSize : function(width, height){
35657 if(!this.ignoreResize(width, height)){
35658 var size = this.adjustForComponents(width, height);
35659 var el = this.layout.getEl();
35660 if (size.height < 1) {
35661 el.setWidth(size.width);
35663 el.setSize(size.width, size.height);
35665 var touch = el.dom.offsetWidth;
35666 this.layout.layout();
35667 // ie requires a double layout on the first pass
35668 if(Roo.isIE && !this.initialized){
35669 this.initialized = true;
35670 this.layout.layout();
35675 // activate all subpanels if not currently active..
35677 setActiveState : function(active){
35678 this.active = active;
35679 this.setActiveClass(active);
35682 this.fireEvent("deactivate", this);
35686 this.fireEvent("activate", this);
35687 // not sure if this should happen before or after..
35688 if (!this.layout) {
35689 return; // should not happen..
35692 for (var r in this.layout.regions) {
35693 reg = this.layout.getRegion(r);
35694 if (reg.getActivePanel()) {
35695 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35696 reg.setActivePanel(reg.getActivePanel());
35699 if (!reg.panels.length) {
35702 reg.showPanel(reg.getPanel(0));
35711 * Returns the nested BorderLayout for this panel
35712 * @return {Roo.BorderLayout}
35714 getLayout : function(){
35715 return this.layout;
35719 * Adds a xtype elements to the layout of the nested panel
35723 xtype : 'ContentPanel',
35730 xtype : 'NestedLayoutPanel',
35736 items : [ ... list of content panels or nested layout panels.. ]
35740 * @param {Object} cfg Xtype definition of item to add.
35742 addxtype : function(cfg) {
35743 return this.layout.addxtype(cfg);
35748 * Ext JS Library 1.1.1
35749 * Copyright(c) 2006-2007, Ext JS, LLC.
35751 * Originally Released Under LGPL - original licence link has changed is not relivant.
35754 * <script type="text/javascript">
35757 * @class Roo.TabPanel
35758 * @extends Roo.util.Observable
35759 * A lightweight tab container.
35763 // basic tabs 1, built from existing content
35764 var tabs = new Roo.TabPanel("tabs1");
35765 tabs.addTab("script", "View Script");
35766 tabs.addTab("markup", "View Markup");
35767 tabs.activate("script");
35769 // more advanced tabs, built from javascript
35770 var jtabs = new Roo.TabPanel("jtabs");
35771 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35773 // set up the UpdateManager
35774 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35775 var updater = tab2.getUpdateManager();
35776 updater.setDefaultUrl("ajax1.htm");
35777 tab2.on('activate', updater.refresh, updater, true);
35779 // Use setUrl for Ajax loading
35780 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35781 tab3.setUrl("ajax2.htm", null, true);
35784 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35787 jtabs.activate("jtabs-1");
35790 * Create a new TabPanel.
35791 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35792 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35794 Roo.bootstrap.panel.Tabs = function(config){
35796 * The container element for this TabPanel.
35797 * @type Roo.Element
35799 this.el = Roo.get(config.el);
35802 if(typeof config == "boolean"){
35803 this.tabPosition = config ? "bottom" : "top";
35805 Roo.apply(this, config);
35809 if(this.tabPosition == "bottom"){
35810 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35811 this.el.addClass("roo-tabs-bottom");
35813 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35814 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35815 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35817 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35819 if(this.tabPosition != "bottom"){
35820 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35821 * @type Roo.Element
35823 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35824 this.el.addClass("roo-tabs-top");
35828 this.bodyEl.setStyle("position", "relative");
35830 this.active = null;
35831 this.activateDelegate = this.activate.createDelegate(this);
35836 * Fires when the active tab changes
35837 * @param {Roo.TabPanel} this
35838 * @param {Roo.TabPanelItem} activePanel The new active tab
35842 * @event beforetabchange
35843 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35844 * @param {Roo.TabPanel} this
35845 * @param {Object} e Set cancel to true on this object to cancel the tab change
35846 * @param {Roo.TabPanelItem} tab The tab being changed to
35848 "beforetabchange" : true
35851 Roo.EventManager.onWindowResize(this.onResize, this);
35852 this.cpad = this.el.getPadding("lr");
35853 this.hiddenCount = 0;
35856 // toolbar on the tabbar support...
35857 if (this.toolbar) {
35858 alert("no toolbar support yet");
35859 this.toolbar = false;
35861 var tcfg = this.toolbar;
35862 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35863 this.toolbar = new Roo.Toolbar(tcfg);
35864 if (Roo.isSafari) {
35865 var tbl = tcfg.container.child('table', true);
35866 tbl.setAttribute('width', '100%');
35874 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35877 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35879 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35881 tabPosition : "top",
35883 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35885 currentTabWidth : 0,
35887 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35891 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35895 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35897 preferredTabWidth : 175,
35899 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35901 resizeTabs : false,
35903 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35905 monitorResize : true,
35907 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35912 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35913 * @param {String} id The id of the div to use <b>or create</b>
35914 * @param {String} text The text for the tab
35915 * @param {String} content (optional) Content to put in the TabPanelItem body
35916 * @param {Boolean} closable (optional) True to create a close icon on the tab
35917 * @return {Roo.TabPanelItem} The created TabPanelItem
35919 addTab : function(id, text, content, closable, tpl)
35921 var item = new Roo.bootstrap.panel.TabItem({
35925 closable : closable,
35928 this.addTabItem(item);
35930 item.setContent(content);
35936 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35937 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35938 * @return {Roo.TabPanelItem}
35940 getTab : function(id){
35941 return this.items[id];
35945 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35946 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35948 hideTab : function(id){
35949 var t = this.items[id];
35952 this.hiddenCount++;
35953 this.autoSizeTabs();
35958 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35959 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35961 unhideTab : function(id){
35962 var t = this.items[id];
35964 t.setHidden(false);
35965 this.hiddenCount--;
35966 this.autoSizeTabs();
35971 * Adds an existing {@link Roo.TabPanelItem}.
35972 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35974 addTabItem : function(item){
35975 this.items[item.id] = item;
35976 this.items.push(item);
35977 // if(this.resizeTabs){
35978 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35979 // this.autoSizeTabs();
35981 // item.autoSize();
35986 * Removes a {@link Roo.TabPanelItem}.
35987 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35989 removeTab : function(id){
35990 var items = this.items;
35991 var tab = items[id];
35992 if(!tab) { return; }
35993 var index = items.indexOf(tab);
35994 if(this.active == tab && items.length > 1){
35995 var newTab = this.getNextAvailable(index);
36000 this.stripEl.dom.removeChild(tab.pnode.dom);
36001 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36002 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36004 items.splice(index, 1);
36005 delete this.items[tab.id];
36006 tab.fireEvent("close", tab);
36007 tab.purgeListeners();
36008 this.autoSizeTabs();
36011 getNextAvailable : function(start){
36012 var items = this.items;
36014 // look for a next tab that will slide over to
36015 // replace the one being removed
36016 while(index < items.length){
36017 var item = items[++index];
36018 if(item && !item.isHidden()){
36022 // if one isn't found select the previous tab (on the left)
36025 var item = items[--index];
36026 if(item && !item.isHidden()){
36034 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36035 * @param {String/Number} id The id or index of the TabPanelItem to disable.
36037 disableTab : function(id){
36038 var tab = this.items[id];
36039 if(tab && this.active != tab){
36045 * Enables a {@link Roo.TabPanelItem} that is disabled.
36046 * @param {String/Number} id The id or index of the TabPanelItem to enable.
36048 enableTab : function(id){
36049 var tab = this.items[id];
36054 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36055 * @param {String/Number} id The id or index of the TabPanelItem to activate.
36056 * @return {Roo.TabPanelItem} The TabPanelItem.
36058 activate : function(id){
36059 var tab = this.items[id];
36063 if(tab == this.active || tab.disabled){
36067 this.fireEvent("beforetabchange", this, e, tab);
36068 if(e.cancel !== true && !tab.disabled){
36070 this.active.hide();
36072 this.active = this.items[id];
36073 this.active.show();
36074 this.fireEvent("tabchange", this, this.active);
36080 * Gets the active {@link Roo.TabPanelItem}.
36081 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36083 getActiveTab : function(){
36084 return this.active;
36088 * Updates the tab body element to fit the height of the container element
36089 * for overflow scrolling
36090 * @param {Number} targetHeight (optional) Override the starting height from the elements height
36092 syncHeight : function(targetHeight){
36093 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36094 var bm = this.bodyEl.getMargins();
36095 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36096 this.bodyEl.setHeight(newHeight);
36100 onResize : function(){
36101 if(this.monitorResize){
36102 this.autoSizeTabs();
36107 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36109 beginUpdate : function(){
36110 this.updating = true;
36114 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36116 endUpdate : function(){
36117 this.updating = false;
36118 this.autoSizeTabs();
36122 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36124 autoSizeTabs : function(){
36125 var count = this.items.length;
36126 var vcount = count - this.hiddenCount;
36127 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36130 var w = Math.max(this.el.getWidth() - this.cpad, 10);
36131 var availWidth = Math.floor(w / vcount);
36132 var b = this.stripBody;
36133 if(b.getWidth() > w){
36134 var tabs = this.items;
36135 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36136 if(availWidth < this.minTabWidth){
36137 /*if(!this.sleft){ // incomplete scrolling code
36138 this.createScrollButtons();
36141 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36144 if(this.currentTabWidth < this.preferredTabWidth){
36145 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36151 * Returns the number of tabs in this TabPanel.
36154 getCount : function(){
36155 return this.items.length;
36159 * Resizes all the tabs to the passed width
36160 * @param {Number} The new width
36162 setTabWidth : function(width){
36163 this.currentTabWidth = width;
36164 for(var i = 0, len = this.items.length; i < len; i++) {
36165 if(!this.items[i].isHidden()) {
36166 this.items[i].setWidth(width);
36172 * Destroys this TabPanel
36173 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36175 destroy : function(removeEl){
36176 Roo.EventManager.removeResizeListener(this.onResize, this);
36177 for(var i = 0, len = this.items.length; i < len; i++){
36178 this.items[i].purgeListeners();
36180 if(removeEl === true){
36181 this.el.update("");
36186 createStrip : function(container)
36188 var strip = document.createElement("nav");
36189 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36190 container.appendChild(strip);
36194 createStripList : function(strip)
36196 // div wrapper for retard IE
36197 // returns the "tr" element.
36198 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36199 //'<div class="x-tabs-strip-wrap">'+
36200 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36201 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36202 return strip.firstChild; //.firstChild.firstChild.firstChild;
36204 createBody : function(container)
36206 var body = document.createElement("div");
36207 Roo.id(body, "tab-body");
36208 //Roo.fly(body).addClass("x-tabs-body");
36209 Roo.fly(body).addClass("tab-content");
36210 container.appendChild(body);
36213 createItemBody :function(bodyEl, id){
36214 var body = Roo.getDom(id);
36216 body = document.createElement("div");
36219 //Roo.fly(body).addClass("x-tabs-item-body");
36220 Roo.fly(body).addClass("tab-pane");
36221 bodyEl.insertBefore(body, bodyEl.firstChild);
36225 createStripElements : function(stripEl, text, closable, tpl)
36227 var td = document.createElement("li"); // was td..
36230 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36233 stripEl.appendChild(td);
36235 td.className = "x-tabs-closable";
36236 if(!this.closeTpl){
36237 this.closeTpl = new Roo.Template(
36238 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36239 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36240 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36243 var el = this.closeTpl.overwrite(td, {"text": text});
36244 var close = el.getElementsByTagName("div")[0];
36245 var inner = el.getElementsByTagName("em")[0];
36246 return {"el": el, "close": close, "inner": inner};
36249 // not sure what this is..
36250 // if(!this.tabTpl){
36251 //this.tabTpl = new Roo.Template(
36252 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36253 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36255 // this.tabTpl = new Roo.Template(
36256 // '<a href="#">' +
36257 // '<span unselectable="on"' +
36258 // (this.disableTooltips ? '' : ' title="{text}"') +
36259 // ' >{text}</span></a>'
36265 var template = tpl || this.tabTpl || false;
36269 template = new Roo.Template(
36271 '<span unselectable="on"' +
36272 (this.disableTooltips ? '' : ' title="{text}"') +
36273 ' >{text}</span></a>'
36277 switch (typeof(template)) {
36281 template = new Roo.Template(template);
36287 var el = template.overwrite(td, {"text": text});
36289 var inner = el.getElementsByTagName("span")[0];
36291 return {"el": el, "inner": inner};
36299 * @class Roo.TabPanelItem
36300 * @extends Roo.util.Observable
36301 * Represents an individual item (tab plus body) in a TabPanel.
36302 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36303 * @param {String} id The id of this TabPanelItem
36304 * @param {String} text The text for the tab of this TabPanelItem
36305 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36307 Roo.bootstrap.panel.TabItem = function(config){
36309 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36310 * @type Roo.TabPanel
36312 this.tabPanel = config.panel;
36314 * The id for this TabPanelItem
36317 this.id = config.id;
36319 this.disabled = false;
36321 this.text = config.text;
36323 this.loaded = false;
36324 this.closable = config.closable;
36327 * The body element for this TabPanelItem.
36328 * @type Roo.Element
36330 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36331 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36332 this.bodyEl.setStyle("display", "block");
36333 this.bodyEl.setStyle("zoom", "1");
36334 //this.hideAction();
36336 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36338 this.el = Roo.get(els.el);
36339 this.inner = Roo.get(els.inner, true);
36340 this.textEl = Roo.get(this.el.dom.firstChild, true);
36341 this.pnode = Roo.get(els.el.parentNode, true);
36342 this.el.on("mousedown", this.onTabMouseDown, this);
36343 this.el.on("click", this.onTabClick, this);
36345 if(config.closable){
36346 var c = Roo.get(els.close, true);
36347 c.dom.title = this.closeText;
36348 c.addClassOnOver("close-over");
36349 c.on("click", this.closeClick, this);
36355 * Fires when this tab becomes the active tab.
36356 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36357 * @param {Roo.TabPanelItem} this
36361 * @event beforeclose
36362 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36363 * @param {Roo.TabPanelItem} this
36364 * @param {Object} e Set cancel to true on this object to cancel the close.
36366 "beforeclose": true,
36369 * Fires when this tab is closed.
36370 * @param {Roo.TabPanelItem} this
36374 * @event deactivate
36375 * Fires when this tab is no longer the active tab.
36376 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36377 * @param {Roo.TabPanelItem} this
36379 "deactivate" : true
36381 this.hidden = false;
36383 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36386 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36388 purgeListeners : function(){
36389 Roo.util.Observable.prototype.purgeListeners.call(this);
36390 this.el.removeAllListeners();
36393 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36396 this.pnode.addClass("active");
36399 this.tabPanel.stripWrap.repaint();
36401 this.fireEvent("activate", this.tabPanel, this);
36405 * Returns true if this tab is the active tab.
36406 * @return {Boolean}
36408 isActive : function(){
36409 return this.tabPanel.getActiveTab() == this;
36413 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36416 this.pnode.removeClass("active");
36418 this.fireEvent("deactivate", this.tabPanel, this);
36421 hideAction : function(){
36422 this.bodyEl.hide();
36423 this.bodyEl.setStyle("position", "absolute");
36424 this.bodyEl.setLeft("-20000px");
36425 this.bodyEl.setTop("-20000px");
36428 showAction : function(){
36429 this.bodyEl.setStyle("position", "relative");
36430 this.bodyEl.setTop("");
36431 this.bodyEl.setLeft("");
36432 this.bodyEl.show();
36436 * Set the tooltip for the tab.
36437 * @param {String} tooltip The tab's tooltip
36439 setTooltip : function(text){
36440 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36441 this.textEl.dom.qtip = text;
36442 this.textEl.dom.removeAttribute('title');
36444 this.textEl.dom.title = text;
36448 onTabClick : function(e){
36449 e.preventDefault();
36450 this.tabPanel.activate(this.id);
36453 onTabMouseDown : function(e){
36454 e.preventDefault();
36455 this.tabPanel.activate(this.id);
36458 getWidth : function(){
36459 return this.inner.getWidth();
36462 setWidth : function(width){
36463 var iwidth = width - this.pnode.getPadding("lr");
36464 this.inner.setWidth(iwidth);
36465 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36466 this.pnode.setWidth(width);
36470 * Show or hide the tab
36471 * @param {Boolean} hidden True to hide or false to show.
36473 setHidden : function(hidden){
36474 this.hidden = hidden;
36475 this.pnode.setStyle("display", hidden ? "none" : "");
36479 * Returns true if this tab is "hidden"
36480 * @return {Boolean}
36482 isHidden : function(){
36483 return this.hidden;
36487 * Returns the text for this tab
36490 getText : function(){
36494 autoSize : function(){
36495 //this.el.beginMeasure();
36496 this.textEl.setWidth(1);
36498 * #2804 [new] Tabs in Roojs
36499 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36501 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36502 //this.el.endMeasure();
36506 * Sets the text for the tab (Note: this also sets the tooltip text)
36507 * @param {String} text The tab's text and tooltip
36509 setText : function(text){
36511 this.textEl.update(text);
36512 this.setTooltip(text);
36513 //if(!this.tabPanel.resizeTabs){
36514 // this.autoSize();
36518 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36520 activate : function(){
36521 this.tabPanel.activate(this.id);
36525 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36527 disable : function(){
36528 if(this.tabPanel.active != this){
36529 this.disabled = true;
36530 this.pnode.addClass("disabled");
36535 * Enables this TabPanelItem if it was previously disabled.
36537 enable : function(){
36538 this.disabled = false;
36539 this.pnode.removeClass("disabled");
36543 * Sets the content for this TabPanelItem.
36544 * @param {String} content The content
36545 * @param {Boolean} loadScripts true to look for and load scripts
36547 setContent : function(content, loadScripts){
36548 this.bodyEl.update(content, loadScripts);
36552 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36553 * @return {Roo.UpdateManager} The UpdateManager
36555 getUpdateManager : function(){
36556 return this.bodyEl.getUpdateManager();
36560 * Set a URL to be used to load the content for this TabPanelItem.
36561 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36562 * @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)
36563 * @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)
36564 * @return {Roo.UpdateManager} The UpdateManager
36566 setUrl : function(url, params, loadOnce){
36567 if(this.refreshDelegate){
36568 this.un('activate', this.refreshDelegate);
36570 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36571 this.on("activate", this.refreshDelegate);
36572 return this.bodyEl.getUpdateManager();
36576 _handleRefresh : function(url, params, loadOnce){
36577 if(!loadOnce || !this.loaded){
36578 var updater = this.bodyEl.getUpdateManager();
36579 updater.update(url, params, this._setLoaded.createDelegate(this));
36584 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36585 * Will fail silently if the setUrl method has not been called.
36586 * This does not activate the panel, just updates its content.
36588 refresh : function(){
36589 if(this.refreshDelegate){
36590 this.loaded = false;
36591 this.refreshDelegate();
36596 _setLoaded : function(){
36597 this.loaded = true;
36601 closeClick : function(e){
36604 this.fireEvent("beforeclose", this, o);
36605 if(o.cancel !== true){
36606 this.tabPanel.removeTab(this.id);
36610 * The text displayed in the tooltip for the close icon.
36613 closeText : "Close this tab"