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);
233 addxtypeChild : function (tree, cntr, is_body)
235 Roo.debug && Roo.log('addxtypeChild:' + cntr);
237 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
241 (typeof(tree['flexy:foreach']) != 'undefined');
245 skip_children = false;
246 // render the element if it's not BODY.
249 cn = Roo.factory(tree);
251 cn.parentType = this.xtype; //??
252 cn.parentId = this.id;
254 var build_from_html = Roo.XComponent.build_from_html;
257 // does the container contain child eleemnts with 'xtype' attributes.
258 // that match this xtype..
259 // note - when we render we create these as well..
260 // so we should check to see if body has xtype set.
261 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263 var self_cntr_el = Roo.get(this[cntr](false));
264 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266 //Roo.log(Roo.XComponent.build_from_html);
267 //Roo.log("got echild:");
270 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
271 // and are not displayed -this causes this to use up the wrong element when matching.
272 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
275 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
276 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
282 //echild.dom.removeAttribute('xtype');
284 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
285 Roo.debug && Roo.log(self_cntr_el);
286 Roo.debug && Roo.log(echild);
287 Roo.debug && Roo.log(cn);
293 // if object has flexy:if - then it may or may not be rendered.
294 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
295 // skip a flexy if element.
296 Roo.debug && Roo.log('skipping render');
297 Roo.debug && Roo.log(tree);
299 Roo.debug && Roo.log('skipping all children');
300 skip_children = true;
305 // actually if flexy:foreach is found, we really want to create
306 // multiple copies here...
308 //Roo.log(this[cntr]());
309 // some elements do not have render methods.. like the layouts...
310 cn.render && cn.render(this[cntr](true));
312 // then add the element..
320 if (typeof (tree.menu) != 'undefined') {
321 tree.menu.parentType = cn.xtype;
322 tree.menu.triggerEl = cn.el;
323 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
327 if (!tree.items || !tree.items.length) {
329 //Roo.log(["no children", this]);
334 var items = tree.items;
337 //Roo.log(items.length);
339 if (!skip_children) {
340 for(var i =0;i < items.length;i++) {
341 // Roo.log(['add child', items[i]]);
342 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
348 //Roo.log("fire childrenrendered");
350 cn.fireEvent('childrenrendered', this);
355 * Show a component - removes 'hidden' class
360 this.el.removeClass('hidden');
364 * Hide a component - adds 'hidden' class
368 if (this.el && !this.el.hasClass('hidden')) {
369 this.el.addClass('hidden');
383 * @class Roo.bootstrap.Body
384 * @extends Roo.bootstrap.Component
385 * Bootstrap Body class
389 * @param {Object} config The config object
392 Roo.bootstrap.Body = function(config){
393 Roo.bootstrap.Body.superclass.constructor.call(this, config);
394 this.el = Roo.get(document.body);
395 if (this.cls && this.cls.length) {
396 Roo.get(document.body).addClass(this.cls);
400 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
402 is_body : true,// just to make sure it's constructed?
407 onRender : function(ct, position)
409 /* Roo.log("Roo.bootstrap.Body - onRender");
410 if (this.cls && this.cls.length) {
411 Roo.get(document.body).addClass(this.cls);
431 * @class Roo.bootstrap.ButtonGroup
432 * @extends Roo.bootstrap.Component
433 * Bootstrap ButtonGroup class
434 * @cfg {String} size lg | sm | xs (default empty normal)
435 * @cfg {String} align vertical | justified (default none)
436 * @cfg {String} direction up | down (default down)
437 * @cfg {Boolean} toolbar false | true
438 * @cfg {Boolean} btn true | false
443 * @param {Object} config The config object
446 Roo.bootstrap.ButtonGroup = function(config){
447 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
458 getAutoCreate : function(){
464 cfg.html = this.html || cfg.html;
475 if (['vertical','justified'].indexOf(this.align)!==-1) {
476 cfg.cls = 'btn-group-' + this.align;
478 if (this.align == 'justified') {
479 console.log(this.items);
483 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
484 cfg.cls += ' btn-group-' + this.size;
487 if (this.direction == 'up') {
488 cfg.cls += ' dropup' ;
504 * @class Roo.bootstrap.Button
505 * @extends Roo.bootstrap.Component
506 * Bootstrap Button class
507 * @cfg {String} html The button content
508 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
509 * @cfg {String} size ( lg | sm | xs)
510 * @cfg {String} tag ( a | input | submit)
511 * @cfg {String} href empty or href
512 * @cfg {Boolean} disabled default false;
513 * @cfg {Boolean} isClose default false;
514 * @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)
515 * @cfg {String} badge text for badge
516 * @cfg {String} theme default
517 * @cfg {Boolean} inverse
518 * @cfg {Boolean} toggle
519 * @cfg {String} ontext text for on toggle state
520 * @cfg {String} offtext text for off toggle state
521 * @cfg {Boolean} defaulton
522 * @cfg {Boolean} preventDefault default true
523 * @cfg {Boolean} removeClass remove the standard class..
524 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
527 * Create a new button
528 * @param {Object} config The config object
532 Roo.bootstrap.Button = function(config){
533 Roo.bootstrap.Button.superclass.constructor.call(this, config);
538 * When a butotn is pressed
539 * @param {Roo.bootstrap.Button} this
540 * @param {Roo.EventObject} e
545 * After the button has been toggles
546 * @param {Roo.EventObject} e
547 * @param {boolean} pressed (also available as button.pressed)
553 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
571 preventDefault: true,
580 getAutoCreate : function(){
588 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
589 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
594 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596 if (this.toggle == true) {
599 cls: 'slider-frame roo-button',
604 'data-off-text':'OFF',
605 cls: 'slider-button',
611 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
612 cfg.cls += ' '+this.weight;
621 cfg["aria-hidden"] = true;
623 cfg.html = "×";
629 if (this.theme==='default') {
630 cfg.cls = 'btn roo-button';
632 //if (this.parentType != 'Navbar') {
633 this.weight = this.weight.length ? this.weight : 'default';
635 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637 cfg.cls += ' btn-' + this.weight;
639 } else if (this.theme==='glow') {
642 cfg.cls = 'btn-glow roo-button';
644 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646 cfg.cls += ' ' + this.weight;
652 this.cls += ' inverse';
657 cfg.cls += ' active';
661 cfg.disabled = 'disabled';
665 Roo.log('changing to ul' );
667 this.glyphicon = 'caret';
670 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672 //gsRoo.log(this.parentType);
673 if (this.parentType === 'Navbar' && !this.parent().bar) {
674 Roo.log('changing to li?');
683 href : this.href || '#'
686 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
687 cfg.cls += ' dropdown';
694 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
696 if (this.glyphicon) {
697 cfg.html = ' ' + cfg.html;
702 cls: 'glyphicon glyphicon-' + this.glyphicon
712 // cfg.cls='btn roo-button';
716 var value = cfg.html;
721 cls: 'glyphicon glyphicon-' + this.glyphicon,
740 cfg.cls += ' dropdown';
741 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
744 if (cfg.tag !== 'a' && this.href !== '') {
745 throw "Tag must be a to set href.";
746 } else if (this.href.length > 0) {
747 cfg.href = this.href;
750 if(this.removeClass){
755 cfg.target = this.target;
760 initEvents: function() {
761 // Roo.log('init events?');
762 // Roo.log(this.el.dom);
765 if (typeof (this.menu) != 'undefined') {
766 this.menu.parentType = this.xtype;
767 this.menu.triggerEl = this.el;
768 this.addxtype(Roo.apply({}, this.menu));
772 if (this.el.hasClass('roo-button')) {
773 this.el.on('click', this.onClick, this);
775 this.el.select('.roo-button').on('click', this.onClick, this);
778 if(this.removeClass){
779 this.el.on('click', this.onClick, this);
782 this.el.enableDisplayMode();
785 onClick : function(e)
792 Roo.log('button on click ');
793 if(this.preventDefault){
796 if (this.pressed === true || this.pressed === false) {
797 this.pressed = !this.pressed;
798 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
799 this.fireEvent('toggle', this, e, this.pressed);
803 this.fireEvent('click', this, e);
807 * Enables this button
811 this.disabled = false;
812 this.el.removeClass('disabled');
816 * Disable this button
820 this.disabled = true;
821 this.el.addClass('disabled');
824 * sets the active state on/off,
825 * @param {Boolean} state (optional) Force a particular state
827 setActive : function(v) {
829 this.el[v ? 'addClass' : 'removeClass']('active');
832 * toggles the current active state
834 toggleActive : function()
836 var active = this.el.hasClass('active');
837 this.setActive(!active);
841 setText : function(str)
843 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
870 * @class Roo.bootstrap.Column
871 * @extends Roo.bootstrap.Component
872 * Bootstrap Column class
873 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
874 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
875 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
876 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
877 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
878 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
879 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
880 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
883 * @cfg {Boolean} hidden (true|false) hide the element
884 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
885 * @cfg {String} fa (ban|check|...) font awesome icon
886 * @cfg {Number} fasize (1|2|....) font awsome size
888 * @cfg {String} icon (info-sign|check|...) glyphicon name
890 * @cfg {String} html content of column.
893 * Create a new Column
894 * @param {Object} config The config object
897 Roo.bootstrap.Column = function(config){
898 Roo.bootstrap.Column.superclass.constructor.call(this, config);
901 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
919 getAutoCreate : function(){
920 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
928 ['xs','sm','md','lg'].map(function(size){
929 //Roo.log( size + ':' + settings[size]);
931 if (settings[size+'off'] !== false) {
932 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
935 if (settings[size] === false) {
939 if (!settings[size]) { // 0 = hidden
940 cfg.cls += ' hidden-' + size;
943 cfg.cls += ' col-' + size + '-' + settings[size];
948 cfg.cls += ' hidden';
951 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
952 cfg.cls +=' alert alert-' + this.alert;
956 if (this.html.length) {
957 cfg.html = this.html;
961 if (this.fasize > 1) {
962 fasize = ' fa-' + this.fasize + 'x';
964 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
969 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
988 * @class Roo.bootstrap.Container
989 * @extends Roo.bootstrap.Component
990 * Bootstrap Container class
991 * @cfg {Boolean} jumbotron is it a jumbotron element
992 * @cfg {String} html content of element
993 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
994 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
995 * @cfg {String} header content of header (for panel)
996 * @cfg {String} footer content of footer (for panel)
997 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
998 * @cfg {String} tag (header|aside|section) type of HTML tag.
999 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1000 * @cfg {String} fa font awesome icon
1001 * @cfg {String} icon (info-sign|check|...) glyphicon name
1002 * @cfg {Boolean} hidden (true|false) hide the element
1003 * @cfg {Boolean} expandable (true|false) default false
1004 * @cfg {Boolean} expanded (true|false) default true
1005 * @cfg {String} rheader contet on the right of header
1006 * @cfg {Boolean} clickable (true|false) default false
1010 * Create a new Container
1011 * @param {Object} config The config object
1014 Roo.bootstrap.Container = function(config){
1015 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1021 * After the panel has been expand
1023 * @param {Roo.bootstrap.Container} this
1028 * After the panel has been collapsed
1030 * @param {Roo.bootstrap.Container} this
1035 * When a element is chick
1036 * @param {Roo.bootstrap.Container} this
1037 * @param {Roo.EventObject} e
1043 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1061 getChildContainer : function() {
1067 if (this.panel.length) {
1068 return this.el.select('.panel-body',true).first();
1075 getAutoCreate : function(){
1078 tag : this.tag || 'div',
1082 if (this.jumbotron) {
1083 cfg.cls = 'jumbotron';
1088 // - this is applied by the parent..
1090 // cfg.cls = this.cls + '';
1093 if (this.sticky.length) {
1095 var bd = Roo.get(document.body);
1096 if (!bd.hasClass('bootstrap-sticky')) {
1097 bd.addClass('bootstrap-sticky');
1098 Roo.select('html',true).setStyle('height', '100%');
1101 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105 if (this.well.length) {
1106 switch (this.well) {
1109 cfg.cls +=' well well-' +this.well;
1118 cfg.cls += ' hidden';
1122 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1123 cfg.cls +=' alert alert-' + this.alert;
1128 if (this.panel.length) {
1129 cfg.cls += ' panel panel-' + this.panel;
1131 if (this.header.length) {
1135 if(this.expandable){
1137 cfg.cls = cfg.cls + ' expandable';
1141 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1149 cls : 'panel-title',
1150 html : (this.expandable ? ' ' : '') + this.header
1154 cls: 'panel-header-right',
1160 cls : 'panel-heading',
1161 style : this.expandable ? 'cursor: pointer' : '',
1169 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1174 if (this.footer.length) {
1176 cls : 'panel-footer',
1185 body.html = this.html || cfg.html;
1186 // prefix with the icons..
1188 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1191 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1196 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1197 cfg.cls = 'container';
1203 initEvents: function()
1205 if(this.expandable){
1206 var headerEl = this.headerEl();
1209 headerEl.on('click', this.onToggleClick, this);
1214 this.el.on('click', this.onClick, this);
1219 onToggleClick : function()
1221 var headerEl = this.headerEl();
1237 if(this.fireEvent('expand', this)) {
1239 this.expanded = true;
1241 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243 this.el.select('.panel-body',true).first().removeClass('hide');
1245 var toggleEl = this.toggleEl();
1251 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1256 collapse : function()
1258 if(this.fireEvent('collapse', this)) {
1260 this.expanded = false;
1262 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1263 this.el.select('.panel-body',true).first().addClass('hide');
1265 var toggleEl = this.toggleEl();
1271 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275 toggleEl : function()
1277 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281 return this.el.select('.panel-heading .fa',true).first();
1284 headerEl : function()
1286 if(!this.el || !this.panel.length || !this.header.length){
1290 return this.el.select('.panel-heading',true).first()
1293 titleEl : function()
1295 if(!this.el || !this.panel.length || !this.header.length){
1299 return this.el.select('.panel-title',true).first();
1302 setTitle : function(v)
1304 var titleEl = this.titleEl();
1310 titleEl.dom.innerHTML = v;
1313 getTitle : function()
1316 var titleEl = this.titleEl();
1322 return titleEl.dom.innerHTML;
1325 setRightTitle : function(v)
1327 var t = this.el.select('.panel-header-right',true).first();
1333 t.dom.innerHTML = v;
1336 onClick : function(e)
1340 this.fireEvent('click', this, e);
1354 * @class Roo.bootstrap.Img
1355 * @extends Roo.bootstrap.Component
1356 * Bootstrap Img class
1357 * @cfg {Boolean} imgResponsive false | true
1358 * @cfg {String} border rounded | circle | thumbnail
1359 * @cfg {String} src image source
1360 * @cfg {String} alt image alternative text
1361 * @cfg {String} href a tag href
1362 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1363 * @cfg {String} xsUrl xs image source
1364 * @cfg {String} smUrl sm image source
1365 * @cfg {String} mdUrl md image source
1366 * @cfg {String} lgUrl lg image source
1369 * Create a new Input
1370 * @param {Object} config The config object
1373 Roo.bootstrap.Img = function(config){
1374 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1380 * The img click event for the img.
1381 * @param {Roo.EventObject} e
1387 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1389 imgResponsive: true,
1399 getAutoCreate : function()
1401 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1402 return this.createSingleImg();
1407 cls: 'roo-image-responsive-group',
1412 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414 if(!_this[size + 'Url']){
1420 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1421 html: _this.html || cfg.html,
1422 src: _this[size + 'Url']
1425 img.cls += ' roo-image-responsive-' + size;
1427 var s = ['xs', 'sm', 'md', 'lg'];
1429 s.splice(s.indexOf(size), 1);
1431 Roo.each(s, function(ss){
1432 img.cls += ' hidden-' + ss;
1435 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1436 cfg.cls += ' img-' + _this.border;
1440 cfg.alt = _this.alt;
1453 a.target = _this.target;
1457 cfg.cn.push((_this.href) ? a : img);
1464 createSingleImg : function()
1468 cls: (this.imgResponsive) ? 'img-responsive' : '',
1470 src : 'about:blank' // just incase src get's set to undefined?!?
1473 cfg.html = this.html || cfg.html;
1475 cfg.src = this.src || cfg.src;
1477 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1478 cfg.cls += ' img-' + this.border;
1495 a.target = this.target;
1500 return (this.href) ? a : cfg;
1503 initEvents: function()
1506 this.el.on('click', this.onClick, this);
1511 onClick : function(e)
1513 Roo.log('img onclick');
1514 this.fireEvent('click', this, e);
1528 * @class Roo.bootstrap.Link
1529 * @extends Roo.bootstrap.Component
1530 * Bootstrap Link Class
1531 * @cfg {String} alt image alternative text
1532 * @cfg {String} href a tag href
1533 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1534 * @cfg {String} html the content of the link.
1535 * @cfg {String} anchor name for the anchor link
1536 * @cfg {String} fa - favicon
1538 * @cfg {Boolean} preventDefault (true | false) default false
1542 * Create a new Input
1543 * @param {Object} config The config object
1546 Roo.bootstrap.Link = function(config){
1547 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1553 * The img click event for the img.
1554 * @param {Roo.EventObject} e
1560 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1564 preventDefault: false,
1570 getAutoCreate : function()
1572 var html = this.html || '';
1574 if (this.fa !== false) {
1575 html = '<i class="fa fa-' + this.fa + '"></i>';
1580 // anchor's do not require html/href...
1581 if (this.anchor === false) {
1583 cfg.href = this.href || '#';
1585 cfg.name = this.anchor;
1586 if (this.html !== false || this.fa !== false) {
1589 if (this.href !== false) {
1590 cfg.href = this.href;
1594 if(this.alt !== false){
1599 if(this.target !== false) {
1600 cfg.target = this.target;
1606 initEvents: function() {
1608 if(!this.href || this.preventDefault){
1609 this.el.on('click', this.onClick, this);
1613 onClick : function(e)
1615 if(this.preventDefault){
1618 //Roo.log('img onclick');
1619 this.fireEvent('click', this, e);
1632 * @class Roo.bootstrap.Header
1633 * @extends Roo.bootstrap.Component
1634 * Bootstrap Header class
1635 * @cfg {String} html content of header
1636 * @cfg {Number} level (1|2|3|4|5|6) default 1
1639 * Create a new Header
1640 * @param {Object} config The config object
1644 Roo.bootstrap.Header = function(config){
1645 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1648 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1656 getAutoCreate : function(){
1661 tag: 'h' + (1 *this.level),
1662 html: this.html || ''
1674 * Ext JS Library 1.1.1
1675 * Copyright(c) 2006-2007, Ext JS, LLC.
1677 * Originally Released Under LGPL - original licence link has changed is not relivant.
1680 * <script type="text/javascript">
1684 * @class Roo.bootstrap.MenuMgr
1685 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1688 Roo.bootstrap.MenuMgr = function(){
1689 var menus, active, groups = {}, attached = false, lastShow = new Date();
1691 // private - called when first menu is created
1694 active = new Roo.util.MixedCollection();
1695 Roo.get(document).addKeyListener(27, function(){
1696 if(active.length > 0){
1704 if(active && active.length > 0){
1705 var c = active.clone();
1715 if(active.length < 1){
1716 Roo.get(document).un("mouseup", onMouseDown);
1724 var last = active.last();
1725 lastShow = new Date();
1728 Roo.get(document).on("mouseup", onMouseDown);
1733 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1734 m.parentMenu.activeChild = m;
1735 }else if(last && last.isVisible()){
1736 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1741 function onBeforeHide(m){
1743 m.activeChild.hide();
1745 if(m.autoHideTimer){
1746 clearTimeout(m.autoHideTimer);
1747 delete m.autoHideTimer;
1752 function onBeforeShow(m){
1753 var pm = m.parentMenu;
1754 if(!pm && !m.allowOtherMenus){
1756 }else if(pm && pm.activeChild && active != m){
1757 pm.activeChild.hide();
1761 // private this should really trigger on mouseup..
1762 function onMouseDown(e){
1763 Roo.log("on Mouse Up");
1765 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1766 Roo.log("MenuManager hideAll");
1775 function onBeforeCheck(mi, state){
1777 var g = groups[mi.group];
1778 for(var i = 0, l = g.length; i < l; i++){
1780 g[i].setChecked(false);
1789 * Hides all menus that are currently visible
1791 hideAll : function(){
1796 register : function(menu){
1800 menus[menu.id] = menu;
1801 menu.on("beforehide", onBeforeHide);
1802 menu.on("hide", onHide);
1803 menu.on("beforeshow", onBeforeShow);
1804 menu.on("show", onShow);
1806 if(g && menu.events["checkchange"]){
1810 groups[g].push(menu);
1811 menu.on("checkchange", onCheck);
1816 * Returns a {@link Roo.menu.Menu} object
1817 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1818 * be used to generate and return a new Menu instance.
1820 get : function(menu){
1821 if(typeof menu == "string"){ // menu id
1823 }else if(menu.events){ // menu instance
1826 /*else if(typeof menu.length == 'number'){ // array of menu items?
1827 return new Roo.bootstrap.Menu({items:menu});
1828 }else{ // otherwise, must be a config
1829 return new Roo.bootstrap.Menu(menu);
1836 unregister : function(menu){
1837 delete menus[menu.id];
1838 menu.un("beforehide", onBeforeHide);
1839 menu.un("hide", onHide);
1840 menu.un("beforeshow", onBeforeShow);
1841 menu.un("show", onShow);
1843 if(g && menu.events["checkchange"]){
1844 groups[g].remove(menu);
1845 menu.un("checkchange", onCheck);
1850 registerCheckable : function(menuItem){
1851 var g = menuItem.group;
1856 groups[g].push(menuItem);
1857 menuItem.on("beforecheckchange", onBeforeCheck);
1862 unregisterCheckable : function(menuItem){
1863 var g = menuItem.group;
1865 groups[g].remove(menuItem);
1866 menuItem.un("beforecheckchange", onBeforeCheck);
1878 * @class Roo.bootstrap.Menu
1879 * @extends Roo.bootstrap.Component
1880 * Bootstrap Menu class - container for MenuItems
1881 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1882 * @cfg {bool} hidden if the menu should be hidden when rendered.
1883 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1884 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1888 * @param {Object} config The config object
1892 Roo.bootstrap.Menu = function(config){
1893 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1894 if (this.registerMenu && this.type != 'treeview') {
1895 Roo.bootstrap.MenuMgr.register(this);
1900 * Fires before this menu is displayed
1901 * @param {Roo.menu.Menu} this
1906 * Fires before this menu is hidden
1907 * @param {Roo.menu.Menu} this
1912 * Fires after this menu is displayed
1913 * @param {Roo.menu.Menu} this
1918 * Fires after this menu is hidden
1919 * @param {Roo.menu.Menu} this
1924 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1925 * @param {Roo.menu.Menu} this
1926 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1927 * @param {Roo.EventObject} e
1932 * Fires when the mouse is hovering over this menu
1933 * @param {Roo.menu.Menu} this
1934 * @param {Roo.EventObject} e
1935 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1940 * Fires when the mouse exits this menu
1941 * @param {Roo.menu.Menu} this
1942 * @param {Roo.EventObject} e
1943 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948 * Fires when a menu item contained in this menu is clicked
1949 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1950 * @param {Roo.EventObject} e
1954 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1957 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1961 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1964 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966 registerMenu : true,
1968 menuItems :false, // stores the menu items..
1978 getChildContainer : function() {
1982 getAutoCreate : function(){
1984 //if (['right'].indexOf(this.align)!==-1) {
1985 // cfg.cn[1].cls += ' pull-right'
1991 cls : 'dropdown-menu' ,
1992 style : 'z-index:1000'
1996 if (this.type === 'submenu') {
1997 cfg.cls = 'submenu active';
1999 if (this.type === 'treeview') {
2000 cfg.cls = 'treeview-menu';
2005 initEvents : function() {
2007 // Roo.log("ADD event");
2008 // Roo.log(this.triggerEl.dom);
2010 this.triggerEl.on('click', this.onTriggerClick, this);
2012 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014 this.triggerEl.addClass('dropdown-toggle');
2017 this.el.on('touchstart' , this.onTouch, this);
2019 this.el.on('click' , this.onClick, this);
2021 this.el.on("mouseover", this.onMouseOver, this);
2022 this.el.on("mouseout", this.onMouseOut, this);
2026 findTargetItem : function(e)
2028 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2032 //Roo.log(t); Roo.log(t.id);
2034 //Roo.log(this.menuitems);
2035 return this.menuitems.get(t.id);
2037 //return this.items.get(t.menuItemId);
2043 onTouch : function(e)
2045 Roo.log("menu.onTouch");
2046 //e.stopEvent(); this make the user popdown broken
2050 onClick : function(e)
2052 Roo.log("menu.onClick");
2054 var t = this.findTargetItem(e);
2055 if(!t || t.isContainer){
2060 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2061 if(t == this.activeItem && t.shouldDeactivate(e)){
2062 this.activeItem.deactivate();
2063 delete this.activeItem;
2067 this.setActiveItem(t, true);
2075 Roo.log('pass click event');
2079 this.fireEvent("click", this, t, e);
2083 (function() { _this.hide(); }).defer(500);
2086 onMouseOver : function(e){
2087 var t = this.findTargetItem(e);
2090 // if(t.canActivate && !t.disabled){
2091 // this.setActiveItem(t, true);
2095 this.fireEvent("mouseover", this, e, t);
2097 isVisible : function(){
2098 return !this.hidden;
2100 onMouseOut : function(e){
2101 var t = this.findTargetItem(e);
2104 // if(t == this.activeItem && t.shouldDeactivate(e)){
2105 // this.activeItem.deactivate();
2106 // delete this.activeItem;
2109 this.fireEvent("mouseout", this, e, t);
2114 * Displays this menu relative to another element
2115 * @param {String/HTMLElement/Roo.Element} element The element to align to
2116 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2117 * the element (defaults to this.defaultAlign)
2118 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120 show : function(el, pos, parentMenu){
2121 this.parentMenu = parentMenu;
2125 this.fireEvent("beforeshow", this);
2126 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2129 * Displays this menu at a specific xy position
2130 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2131 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133 showAt : function(xy, parentMenu, /* private: */_e){
2134 this.parentMenu = parentMenu;
2139 this.fireEvent("beforeshow", this);
2140 //xy = this.el.adjustForConstraints(xy);
2144 this.hideMenuItems();
2145 this.hidden = false;
2146 this.triggerEl.addClass('open');
2148 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2149 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2152 if(this.el.getStyle('top').slice(-1) != "%"){
2157 this.fireEvent("show", this);
2163 this.doFocus.defer(50, this);
2167 doFocus : function(){
2169 this.focusEl.focus();
2174 * Hides this menu and optionally all parent menus
2175 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2177 hide : function(deep)
2180 this.hideMenuItems();
2181 if(this.el && this.isVisible()){
2182 this.fireEvent("beforehide", this);
2183 if(this.activeItem){
2184 this.activeItem.deactivate();
2185 this.activeItem = null;
2187 this.triggerEl.removeClass('open');;
2189 this.fireEvent("hide", this);
2191 if(deep === true && this.parentMenu){
2192 this.parentMenu.hide(true);
2196 onTriggerClick : function(e)
2198 Roo.log('trigger click');
2200 var target = e.getTarget();
2202 Roo.log(target.nodeName.toLowerCase());
2204 if(target.nodeName.toLowerCase() === 'i'){
2210 onTriggerPress : function(e)
2212 Roo.log('trigger press');
2213 //Roo.log(e.getTarget());
2214 // Roo.log(this.triggerEl.dom);
2216 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2217 var pel = Roo.get(e.getTarget());
2218 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2219 Roo.log('is treeview or dropdown?');
2223 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2227 if (this.isVisible()) {
2232 this.show(this.triggerEl, false, false);
2235 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2242 hideMenuItems : function()
2244 Roo.log("hide Menu Items");
2248 //$(backdrop).remove()
2249 this.el.select('.open',true).each(function(aa) {
2251 aa.removeClass('open');
2252 //var parent = getParent($(this))
2253 //var relatedTarget = { relatedTarget: this }
2255 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2256 //if (e.isDefaultPrevented()) return
2257 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2260 addxtypeChild : function (tree, cntr) {
2261 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2263 this.menuitems.add(comp);
2284 * @class Roo.bootstrap.MenuItem
2285 * @extends Roo.bootstrap.Component
2286 * Bootstrap MenuItem class
2287 * @cfg {String} html the menu label
2288 * @cfg {String} href the link
2289 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2290 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2291 * @cfg {Boolean} active used on sidebars to highlight active itesm
2292 * @cfg {String} fa favicon to show on left of menu item.
2293 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2297 * Create a new MenuItem
2298 * @param {Object} config The config object
2302 Roo.bootstrap.MenuItem = function(config){
2303 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2308 * The raw click event for the entire grid.
2309 * @param {Roo.bootstrap.MenuItem} this
2310 * @param {Roo.EventObject} e
2316 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2320 preventDefault: true,
2321 isContainer : false,
2325 getAutoCreate : function(){
2327 if(this.isContainer){
2330 cls: 'dropdown-menu-item'
2344 if (this.fa !== false) {
2347 cls : 'fa fa-' + this.fa
2356 cls: 'dropdown-menu-item',
2359 if (this.parent().type == 'treeview') {
2360 cfg.cls = 'treeview-menu';
2363 cfg.cls += ' active';
2368 anc.href = this.href || cfg.cn[0].href ;
2369 ctag.html = this.html || cfg.cn[0].html ;
2373 initEvents: function()
2375 if (this.parent().type == 'treeview') {
2376 this.el.select('a').on('click', this.onClick, this);
2379 this.menu.parentType = this.xtype;
2380 this.menu.triggerEl = this.el;
2381 this.menu = this.addxtype(Roo.apply({}, this.menu));
2385 onClick : function(e)
2387 Roo.log('item on click ');
2388 //if(this.preventDefault){
2389 // e.preventDefault();
2391 //this.parent().hideMenuItems();
2393 this.fireEvent('click', this, e);
2412 * @class Roo.bootstrap.MenuSeparator
2413 * @extends Roo.bootstrap.Component
2414 * Bootstrap MenuSeparator class
2417 * Create a new MenuItem
2418 * @param {Object} config The config object
2422 Roo.bootstrap.MenuSeparator = function(config){
2423 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2426 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2428 getAutoCreate : function(){
2447 * @class Roo.bootstrap.Modal
2448 * @extends Roo.bootstrap.Component
2449 * Bootstrap Modal class
2450 * @cfg {String} title Title of dialog
2451 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2452 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2453 * @cfg {Boolean} specificTitle default false
2454 * @cfg {Array} buttons Array of buttons or standard button set..
2455 * @cfg {String} buttonPosition (left|right|center) default right
2456 * @cfg {Boolean} animate default true
2457 * @cfg {Boolean} allow_close default true
2460 * Create a new Modal Dialog
2461 * @param {Object} config The config object
2464 Roo.bootstrap.Modal = function(config){
2465 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2470 * The raw btnclick event for the button
2471 * @param {Roo.EventObject} e
2475 this.buttons = this.buttons || [];
2478 this.tmpl = Roo.factory(this.tmpl);
2483 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2485 title : 'test dialog',
2495 specificTitle: false,
2497 buttonPosition: 'right',
2511 onRender : function(ct, position)
2513 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2516 var cfg = Roo.apply({}, this.getAutoCreate());
2519 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2521 //if (!cfg.name.length) {
2525 cfg.cls += ' ' + this.cls;
2528 cfg.style = this.style;
2530 this.el = Roo.get(document.body).createChild(cfg, position);
2532 //var type = this.el.dom.type;
2535 if(this.tabIndex !== undefined){
2536 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2540 this.bodyEl = this.el.select('.modal-body',true).first();
2541 this.closeEl = this.el.select('.modal-header .close', true).first();
2542 this.footerEl = this.el.select('.modal-footer',true).first();
2543 this.titleEl = this.el.select('.modal-title',true).first();
2547 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2548 this.maskEl.enableDisplayMode("block");
2550 //this.el.addClass("x-dlg-modal");
2552 if (this.buttons.length) {
2553 Roo.each(this.buttons, function(bb) {
2554 var b = Roo.apply({}, bb);
2555 b.xns = b.xns || Roo.bootstrap;
2556 b.xtype = b.xtype || 'Button';
2557 if (typeof(b.listeners) == 'undefined') {
2558 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2561 var btn = Roo.factory(b);
2563 btn.render(this.el.select('.modal-footer div').first());
2567 // render the children.
2570 if(typeof(this.items) != 'undefined'){
2571 var items = this.items;
2574 for(var i =0;i < items.length;i++) {
2575 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2579 this.items = nitems;
2581 // where are these used - they used to be body/close/footer
2585 //this.el.addClass([this.fieldClass, this.cls]);
2589 getAutoCreate : function(){
2594 html : this.html || ''
2599 cls : 'modal-title',
2603 if(this.specificTitle){
2609 if (this.allow_close) {
2620 style : 'display: none',
2623 cls: "modal-dialog",
2626 cls : "modal-content",
2629 cls : 'modal-header',
2634 cls : 'modal-footer',
2638 cls: 'btn-' + this.buttonPosition
2655 modal.cls += ' fade';
2661 getChildContainer : function() {
2666 getButtonContainer : function() {
2667 return this.el.select('.modal-footer div',true).first();
2670 initEvents : function()
2672 if (this.allow_close) {
2673 this.closeEl.on('click', this.hide, this);
2678 window.addEventListener("resize", function() { _this.resize(); } );
2684 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2689 if (!this.rendered) {
2693 this.el.setStyle('display', 'block');
2695 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2698 this.el.addClass('in');
2701 this.el.addClass('in');
2705 // not sure how we can show data in here..
2707 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2710 Roo.get(document.body).addClass("x-body-masked");
2711 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2713 this.el.setStyle('zIndex', '10001');
2715 this.fireEvent('show', this);
2723 Roo.get(document.body).removeClass("x-body-masked");
2724 this.el.removeClass('in');
2725 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2727 if(this.animate){ // why
2729 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2731 this.el.setStyle('display', 'none');
2734 this.fireEvent('hide', this);
2737 addButton : function(str, cb)
2741 var b = Roo.apply({}, { html : str } );
2742 b.xns = b.xns || Roo.bootstrap;
2743 b.xtype = b.xtype || 'Button';
2744 if (typeof(b.listeners) == 'undefined') {
2745 b.listeners = { click : cb.createDelegate(this) };
2748 var btn = Roo.factory(b);
2750 btn.render(this.el.select('.modal-footer div').first());
2756 setDefaultButton : function(btn)
2758 //this.el.select('.modal-footer').()
2762 resizeTo: function(w,h)
2766 this.el.select('.modal-dialog',true).first().setWidth(w);
2767 if (this.diff === false) {
2768 this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2771 this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2775 setContentSize : function(w, h)
2779 onButtonClick: function(btn,e)
2782 this.fireEvent('btnclick', btn.name, e);
2785 * Set the title of the Dialog
2786 * @param {String} str new Title
2788 setTitle: function(str) {
2789 this.titleEl.dom.innerHTML = str;
2792 * Set the body of the Dialog
2793 * @param {String} str new Title
2795 setBody: function(str) {
2796 this.bodyEl.dom.innerHTML = str;
2799 * Set the body of the Dialog using the template
2800 * @param {Obj} data - apply this data to the template and replace the body contents.
2802 applyBody: function(obj)
2805 Roo.log("Error - using apply Body without a template");
2808 this.tmpl.overwrite(this.bodyEl, obj);
2814 Roo.apply(Roo.bootstrap.Modal, {
2816 * Button config that displays a single OK button
2825 * Button config that displays Yes and No buttons
2841 * Button config that displays OK and Cancel buttons
2856 * Button config that displays Yes, No and Cancel buttons
2879 * messagebox - can be used as a replace
2883 * @class Roo.MessageBox
2884 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2888 Roo.Msg.alert('Status', 'Changes saved successfully.');
2890 // Prompt for user data:
2891 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2893 // process text value...
2897 // Show a dialog using config options:
2899 title:'Save Changes?',
2900 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2901 buttons: Roo.Msg.YESNOCANCEL,
2908 Roo.bootstrap.MessageBox = function(){
2909 var dlg, opt, mask, waitTimer;
2910 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2911 var buttons, activeTextEl, bwidth;
2915 var handleButton = function(button){
2917 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2921 var handleHide = function(){
2923 dlg.el.removeClass(opt.cls);
2926 // Roo.TaskMgr.stop(waitTimer);
2927 // waitTimer = null;
2932 var updateButtons = function(b){
2935 buttons["ok"].hide();
2936 buttons["cancel"].hide();
2937 buttons["yes"].hide();
2938 buttons["no"].hide();
2939 //dlg.footer.dom.style.display = 'none';
2942 dlg.footerEl.dom.style.display = '';
2943 for(var k in buttons){
2944 if(typeof buttons[k] != "function"){
2947 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2948 width += buttons[k].el.getWidth()+15;
2958 var handleEsc = function(d, k, e){
2959 if(opt && opt.closable !== false){
2969 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2970 * @return {Roo.BasicDialog} The BasicDialog element
2972 getDialog : function(){
2974 dlg = new Roo.bootstrap.Modal( {
2977 //constraintoviewport:false,
2979 //collapsible : false,
2984 //buttonAlign:"center",
2985 closeClick : function(){
2986 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2989 handleButton("cancel");
2994 dlg.on("hide", handleHide);
2996 //dlg.addKeyListener(27, handleEsc);
2998 this.buttons = buttons;
2999 var bt = this.buttonText;
3000 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3001 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3002 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3003 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3005 bodyEl = dlg.bodyEl.createChild({
3007 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3008 '<textarea class="roo-mb-textarea"></textarea>' +
3009 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3011 msgEl = bodyEl.dom.firstChild;
3012 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3013 textboxEl.enableDisplayMode();
3014 textboxEl.addKeyListener([10,13], function(){
3015 if(dlg.isVisible() && opt && opt.buttons){
3018 }else if(opt.buttons.yes){
3019 handleButton("yes");
3023 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3024 textareaEl.enableDisplayMode();
3025 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3026 progressEl.enableDisplayMode();
3027 var pf = progressEl.dom.firstChild;
3029 pp = Roo.get(pf.firstChild);
3030 pp.setHeight(pf.offsetHeight);
3038 * Updates the message box body text
3039 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3040 * the XHTML-compliant non-breaking space character '&#160;')
3041 * @return {Roo.MessageBox} This message box
3043 updateText : function(text){
3044 if(!dlg.isVisible() && !opt.width){
3045 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3047 msgEl.innerHTML = text || ' ';
3049 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3050 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3052 Math.min(opt.width || cw , this.maxWidth),
3053 Math.max(opt.minWidth || this.minWidth, bwidth)
3056 activeTextEl.setWidth(w);
3058 if(dlg.isVisible()){
3059 dlg.fixedcenter = false;
3061 // to big, make it scroll. = But as usual stupid IE does not support
3064 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3065 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3066 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3068 bodyEl.dom.style.height = '';
3069 bodyEl.dom.style.overflowY = '';
3072 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3074 bodyEl.dom.style.overflowX = '';
3077 dlg.setContentSize(w, bodyEl.getHeight());
3078 if(dlg.isVisible()){
3079 dlg.fixedcenter = true;
3085 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3086 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3087 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3088 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3089 * @return {Roo.MessageBox} This message box
3091 updateProgress : function(value, text){
3093 this.updateText(text);
3095 if (pp) { // weird bug on my firefox - for some reason this is not defined
3096 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3102 * Returns true if the message box is currently displayed
3103 * @return {Boolean} True if the message box is visible, else false
3105 isVisible : function(){
3106 return dlg && dlg.isVisible();
3110 * Hides the message box if it is displayed
3113 if(this.isVisible()){
3119 * Displays a new message box, or reinitializes an existing message box, based on the config options
3120 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3121 * The following config object properties are supported:
3123 Property Type Description
3124 ---------- --------------- ------------------------------------------------------------------------------------
3125 animEl String/Element An id or Element from which the message box should animate as it opens and
3126 closes (defaults to undefined)
3127 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3128 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3129 closable Boolean False to hide the top-right close button (defaults to true). Note that
3130 progress and wait dialogs will ignore this property and always hide the
3131 close button as they can only be closed programmatically.
3132 cls String A custom CSS class to apply to the message box element
3133 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3134 displayed (defaults to 75)
3135 fn Function A callback function to execute after closing the dialog. The arguments to the
3136 function will be btn (the name of the button that was clicked, if applicable,
3137 e.g. "ok"), and text (the value of the active text field, if applicable).
3138 Progress and wait dialogs will ignore this option since they do not respond to
3139 user actions and can only be closed programmatically, so any required function
3140 should be called by the same code after it closes the dialog.
3141 icon String A CSS class that provides a background image to be used as an icon for
3142 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3143 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3144 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3145 modal Boolean False to allow user interaction with the page while the message box is
3146 displayed (defaults to true)
3147 msg String A string that will replace the existing message box body text (defaults
3148 to the XHTML-compliant non-breaking space character ' ')
3149 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3150 progress Boolean True to display a progress bar (defaults to false)
3151 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3152 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3153 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3154 title String The title text
3155 value String The string value to set into the active textbox element if displayed
3156 wait Boolean True to display a progress bar (defaults to false)
3157 width Number The width of the dialog in pixels
3164 msg: 'Please enter your address:',
3166 buttons: Roo.MessageBox.OKCANCEL,
3169 animEl: 'addAddressBtn'
3172 * @param {Object} config Configuration options
3173 * @return {Roo.MessageBox} This message box
3175 show : function(options)
3178 // this causes nightmares if you show one dialog after another
3179 // especially on callbacks..
3181 if(this.isVisible()){
3184 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3185 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3186 Roo.log("New Dialog Message:" + options.msg )
3187 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3188 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3191 var d = this.getDialog();
3193 d.setTitle(opt.title || " ");
3194 d.closeEl.setDisplayed(opt.closable !== false);
3195 activeTextEl = textboxEl;
3196 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3201 textareaEl.setHeight(typeof opt.multiline == "number" ?
3202 opt.multiline : this.defaultTextHeight);
3203 activeTextEl = textareaEl;
3212 progressEl.setDisplayed(opt.progress === true);
3213 this.updateProgress(0);
3214 activeTextEl.dom.value = opt.value || "";
3216 dlg.setDefaultButton(activeTextEl);
3218 var bs = opt.buttons;
3222 }else if(bs && bs.yes){
3223 db = buttons["yes"];
3225 dlg.setDefaultButton(db);
3227 bwidth = updateButtons(opt.buttons);
3228 this.updateText(opt.msg);
3230 d.el.addClass(opt.cls);
3232 d.proxyDrag = opt.proxyDrag === true;
3233 d.modal = opt.modal !== false;
3234 d.mask = opt.modal !== false ? mask : false;
3236 // force it to the end of the z-index stack so it gets a cursor in FF
3237 document.body.appendChild(dlg.el.dom);
3238 d.animateTarget = null;
3239 d.show(options.animEl);
3245 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3246 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3247 * and closing the message box when the process is complete.
3248 * @param {String} title The title bar text
3249 * @param {String} msg The message box body text
3250 * @return {Roo.MessageBox} This message box
3252 progress : function(title, msg){
3259 minWidth: this.minProgressWidth,
3266 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3267 * If a callback function is passed it will be called after the user clicks the button, and the
3268 * id of the button that was clicked will be passed as the only parameter to the callback
3269 * (could also be the top-right close button).
3270 * @param {String} title The title bar text
3271 * @param {String} msg The message box body text
3272 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3273 * @param {Object} scope (optional) The scope of the callback function
3274 * @return {Roo.MessageBox} This message box
3276 alert : function(title, msg, fn, scope){
3289 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3290 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3291 * You are responsible for closing the message box when the process is complete.
3292 * @param {String} msg The message box body text
3293 * @param {String} title (optional) The title bar text
3294 * @return {Roo.MessageBox} This message box
3296 wait : function(msg, title){
3307 waitTimer = Roo.TaskMgr.start({
3309 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3317 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3318 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3319 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3320 * @param {String} title The title bar text
3321 * @param {String} msg The message box body text
3322 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3323 * @param {Object} scope (optional) The scope of the callback function
3324 * @return {Roo.MessageBox} This message box
3326 confirm : function(title, msg, fn, scope){
3330 buttons: this.YESNO,
3339 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3340 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3341 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3342 * (could also be the top-right close button) and the text that was entered will be passed as the two
3343 * parameters to the callback.
3344 * @param {String} title The title bar text
3345 * @param {String} msg The message box body text
3346 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3347 * @param {Object} scope (optional) The scope of the callback function
3348 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3349 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3350 * @return {Roo.MessageBox} This message box
3352 prompt : function(title, msg, fn, scope, multiline){
3356 buttons: this.OKCANCEL,
3361 multiline: multiline,
3368 * Button config that displays a single OK button
3373 * Button config that displays Yes and No buttons
3376 YESNO : {yes:true, no:true},
3378 * Button config that displays OK and Cancel buttons
3381 OKCANCEL : {ok:true, cancel:true},
3383 * Button config that displays Yes, No and Cancel buttons
3386 YESNOCANCEL : {yes:true, no:true, cancel:true},
3389 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3392 defaultTextHeight : 75,
3394 * The maximum width in pixels of the message box (defaults to 600)
3399 * The minimum width in pixels of the message box (defaults to 100)
3404 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3405 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3408 minProgressWidth : 250,
3410 * An object containing the default button text strings that can be overriden for localized language support.
3411 * Supported properties are: ok, cancel, yes and no.
3412 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3425 * Shorthand for {@link Roo.MessageBox}
3427 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3428 Roo.Msg = Roo.Msg || Roo.MessageBox;
3437 * @class Roo.bootstrap.Navbar
3438 * @extends Roo.bootstrap.Component
3439 * Bootstrap Navbar class
3442 * Create a new Navbar
3443 * @param {Object} config The config object
3447 Roo.bootstrap.Navbar = function(config){
3448 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3452 * @event beforetoggle
3453 * Fire before toggle the menu
3454 * @param {Roo.EventObject} e
3456 "beforetoggle" : true
3460 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3469 getAutoCreate : function(){
3472 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3476 initEvents :function ()
3478 //Roo.log(this.el.select('.navbar-toggle',true));
3479 this.el.select('.navbar-toggle',true).on('click', function() {
3480 if(this.fireEvent('beforetoggle', this) !== false){
3481 this.el.select('.navbar-collapse',true).toggleClass('in');
3491 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3493 var size = this.el.getSize();
3494 this.maskEl.setSize(size.width, size.height);
3495 this.maskEl.enableDisplayMode("block");
3504 getChildContainer : function()
3506 if (this.el.select('.collapse').getCount()) {
3507 return this.el.select('.collapse',true).first();
3540 * @class Roo.bootstrap.NavSimplebar
3541 * @extends Roo.bootstrap.Navbar
3542 * Bootstrap Sidebar class
3544 * @cfg {Boolean} inverse is inverted color
3546 * @cfg {String} type (nav | pills | tabs)
3547 * @cfg {Boolean} arrangement stacked | justified
3548 * @cfg {String} align (left | right) alignment
3550 * @cfg {Boolean} main (true|false) main nav bar? default false
3551 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3553 * @cfg {String} tag (header|footer|nav|div) default is nav
3559 * Create a new Sidebar
3560 * @param {Object} config The config object
3564 Roo.bootstrap.NavSimplebar = function(config){
3565 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3568 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3584 getAutoCreate : function(){
3588 tag : this.tag || 'div',
3601 this.type = this.type || 'nav';
3602 if (['tabs','pills'].indexOf(this.type)!==-1) {
3603 cfg.cn[0].cls += ' nav-' + this.type
3607 if (this.type!=='nav') {
3608 Roo.log('nav type must be nav/tabs/pills')
3610 cfg.cn[0].cls += ' navbar-nav'
3616 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3617 cfg.cn[0].cls += ' nav-' + this.arrangement;
3621 if (this.align === 'right') {
3622 cfg.cn[0].cls += ' navbar-right';
3626 cfg.cls += ' navbar-inverse';
3653 * @class Roo.bootstrap.NavHeaderbar
3654 * @extends Roo.bootstrap.NavSimplebar
3655 * Bootstrap Sidebar class
3657 * @cfg {String} brand what is brand
3658 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3659 * @cfg {String} brand_href href of the brand
3660 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3661 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3662 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3663 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3666 * Create a new Sidebar
3667 * @param {Object} config The config object
3671 Roo.bootstrap.NavHeaderbar = function(config){
3672 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3676 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3683 desktopCenter : false,
3686 getAutoCreate : function(){
3689 tag: this.nav || 'nav',
3696 if (this.desktopCenter) {
3697 cn.push({cls : 'container', cn : []});
3704 cls: 'navbar-header',
3709 cls: 'navbar-toggle',
3710 'data-toggle': 'collapse',
3715 html: 'Toggle navigation'
3737 cls: 'collapse navbar-collapse',
3741 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3743 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3744 cfg.cls += ' navbar-' + this.position;
3746 // tag can override this..
3748 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3751 if (this.brand !== '') {
3754 href: this.brand_href ? this.brand_href : '#',
3755 cls: 'navbar-brand',
3763 cfg.cls += ' main-nav';
3771 getHeaderChildContainer : function()
3773 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3774 return this.el.select('.navbar-header',true).first();
3777 return this.getChildContainer();
3781 initEvents : function()
3783 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3785 if (this.autohide) {
3790 Roo.get(document).on('scroll',function(e) {
3791 var ns = Roo.get(document).getScroll().top;
3792 var os = prevScroll;
3796 ft.removeClass('slideDown');
3797 ft.addClass('slideUp');
3800 ft.removeClass('slideUp');
3801 ft.addClass('slideDown');
3822 * @class Roo.bootstrap.NavSidebar
3823 * @extends Roo.bootstrap.Navbar
3824 * Bootstrap Sidebar class
3827 * Create a new Sidebar
3828 * @param {Object} config The config object
3832 Roo.bootstrap.NavSidebar = function(config){
3833 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3836 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3838 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3840 getAutoCreate : function(){
3845 cls: 'sidebar sidebar-nav'
3867 * @class Roo.bootstrap.NavGroup
3868 * @extends Roo.bootstrap.Component
3869 * Bootstrap NavGroup class
3870 * @cfg {String} align (left|right)
3871 * @cfg {Boolean} inverse
3872 * @cfg {String} type (nav|pills|tab) default nav
3873 * @cfg {String} navId - reference Id for navbar.
3877 * Create a new nav group
3878 * @param {Object} config The config object
3881 Roo.bootstrap.NavGroup = function(config){
3882 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3885 Roo.bootstrap.NavGroup.register(this);
3889 * Fires when the active item changes
3890 * @param {Roo.bootstrap.NavGroup} this
3891 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3892 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3899 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3910 getAutoCreate : function()
3912 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3919 if (['tabs','pills'].indexOf(this.type)!==-1) {
3920 cfg.cls += ' nav-' + this.type
3922 if (this.type!=='nav') {
3923 Roo.log('nav type must be nav/tabs/pills')
3925 cfg.cls += ' navbar-nav'
3928 if (this.parent().sidebar) {
3931 cls: 'dashboard-menu sidebar-menu'
3937 if (this.form === true) {
3943 if (this.align === 'right') {
3944 cfg.cls += ' navbar-right';
3946 cfg.cls += ' navbar-left';
3950 if (this.align === 'right') {
3951 cfg.cls += ' navbar-right';
3955 cfg.cls += ' navbar-inverse';
3963 * sets the active Navigation item
3964 * @param {Roo.bootstrap.NavItem} the new current navitem
3966 setActiveItem : function(item)
3969 Roo.each(this.navItems, function(v){
3974 v.setActive(false, true);
3981 item.setActive(true, true);
3982 this.fireEvent('changed', this, item, prev);
3987 * gets the active Navigation item
3988 * @return {Roo.bootstrap.NavItem} the current navitem
3990 getActive : function()
3994 Roo.each(this.navItems, function(v){
4005 indexOfNav : function()
4009 Roo.each(this.navItems, function(v,i){
4020 * adds a Navigation item
4021 * @param {Roo.bootstrap.NavItem} the navitem to add
4023 addItem : function(cfg)
4025 var cn = new Roo.bootstrap.NavItem(cfg);
4027 cn.parentId = this.id;
4028 cn.onRender(this.el, null);
4032 * register a Navigation item
4033 * @param {Roo.bootstrap.NavItem} the navitem to add
4035 register : function(item)
4037 this.navItems.push( item);
4038 item.navId = this.navId;
4043 * clear all the Navigation item
4046 clearAll : function()
4049 this.el.dom.innerHTML = '';
4052 getNavItem: function(tabId)
4055 Roo.each(this.navItems, function(e) {
4056 if (e.tabId == tabId) {
4066 setActiveNext : function()
4068 var i = this.indexOfNav(this.getActive());
4069 if (i > this.navItems.length) {
4072 this.setActiveItem(this.navItems[i+1]);
4074 setActivePrev : function()
4076 var i = this.indexOfNav(this.getActive());
4080 this.setActiveItem(this.navItems[i-1]);
4082 clearWasActive : function(except) {
4083 Roo.each(this.navItems, function(e) {
4084 if (e.tabId != except.tabId && e.was_active) {
4085 e.was_active = false;
4092 getWasActive : function ()
4095 Roo.each(this.navItems, function(e) {
4110 Roo.apply(Roo.bootstrap.NavGroup, {
4114 * register a Navigation Group
4115 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4117 register : function(navgrp)
4119 this.groups[navgrp.navId] = navgrp;
4123 * fetch a Navigation Group based on the navigation ID
4124 * @param {string} the navgroup to add
4125 * @returns {Roo.bootstrap.NavGroup} the navgroup
4127 get: function(navId) {
4128 if (typeof(this.groups[navId]) == 'undefined') {
4130 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4132 return this.groups[navId] ;
4147 * @class Roo.bootstrap.NavItem
4148 * @extends Roo.bootstrap.Component
4149 * Bootstrap Navbar.NavItem class
4150 * @cfg {String} href link to
4151 * @cfg {String} html content of button
4152 * @cfg {String} badge text inside badge
4153 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4154 * @cfg {String} glyphicon name of glyphicon
4155 * @cfg {String} icon name of font awesome icon
4156 * @cfg {Boolean} active Is item active
4157 * @cfg {Boolean} disabled Is item disabled
4159 * @cfg {Boolean} preventDefault (true | false) default false
4160 * @cfg {String} tabId the tab that this item activates.
4161 * @cfg {String} tagtype (a|span) render as a href or span?
4162 * @cfg {Boolean} animateRef (true|false) link to element default false
4165 * Create a new Navbar Item
4166 * @param {Object} config The config object
4168 Roo.bootstrap.NavItem = function(config){
4169 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4174 * The raw click event for the entire grid.
4175 * @param {Roo.EventObject} e
4180 * Fires when the active item active state changes
4181 * @param {Roo.bootstrap.NavItem} this
4182 * @param {boolean} state the new state
4188 * Fires when scroll to element
4189 * @param {Roo.bootstrap.NavItem} this
4190 * @param {Object} options
4191 * @param {Roo.EventObject} e
4199 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4207 preventDefault : false,
4214 getAutoCreate : function(){
4223 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4225 if (this.disabled) {
4226 cfg.cls += ' disabled';
4229 if (this.href || this.html || this.glyphicon || this.icon) {
4233 href : this.href || "#",
4234 html: this.html || ''
4239 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4242 if(this.glyphicon) {
4243 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4248 cfg.cn[0].html += " <span class='caret'></span>";
4252 if (this.badge !== '') {
4254 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4262 initEvents: function()
4264 if (typeof (this.menu) != 'undefined') {
4265 this.menu.parentType = this.xtype;
4266 this.menu.triggerEl = this.el;
4267 this.menu = this.addxtype(Roo.apply({}, this.menu));
4270 this.el.select('a',true).on('click', this.onClick, this);
4272 if(this.tagtype == 'span'){
4273 this.el.select('span',true).on('click', this.onClick, this);
4276 // at this point parent should be available..
4277 this.parent().register(this);
4280 onClick : function(e)
4282 if (e.getTarget('.dropdown-menu-item')) {
4283 // did you click on a menu itemm.... - then don't trigger onclick..
4288 this.preventDefault ||
4291 Roo.log("NavItem - prevent Default?");
4295 if (this.disabled) {
4299 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4300 if (tg && tg.transition) {
4301 Roo.log("waiting for the transitionend");
4307 //Roo.log("fire event clicked");
4308 if(this.fireEvent('click', this, e) === false){
4312 if(this.tagtype == 'span'){
4316 //Roo.log(this.href);
4317 var ael = this.el.select('a',true).first();
4320 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4321 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4322 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4323 return; // ignore... - it's a 'hash' to another page.
4325 Roo.log("NavItem - prevent Default?");
4327 this.scrollToElement(e);
4331 var p = this.parent();
4333 if (['tabs','pills'].indexOf(p.type)!==-1) {
4334 if (typeof(p.setActiveItem) !== 'undefined') {
4335 p.setActiveItem(this);
4339 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4340 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4341 // remove the collapsed menu expand...
4342 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4346 isActive: function () {
4349 setActive : function(state, fire, is_was_active)
4351 if (this.active && !state && this.navId) {
4352 this.was_active = true;
4353 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4355 nv.clearWasActive(this);
4359 this.active = state;
4362 this.el.removeClass('active');
4363 } else if (!this.el.hasClass('active')) {
4364 this.el.addClass('active');
4367 this.fireEvent('changed', this, state);
4370 // show a panel if it's registered and related..
4372 if (!this.navId || !this.tabId || !state || is_was_active) {
4376 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4380 var pan = tg.getPanelByName(this.tabId);
4384 // if we can not flip to new panel - go back to old nav highlight..
4385 if (false == tg.showPanel(pan)) {
4386 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4388 var onav = nv.getWasActive();
4390 onav.setActive(true, false, true);
4399 // this should not be here...
4400 setDisabled : function(state)
4402 this.disabled = state;
4404 this.el.removeClass('disabled');
4405 } else if (!this.el.hasClass('disabled')) {
4406 this.el.addClass('disabled');
4412 * Fetch the element to display the tooltip on.
4413 * @return {Roo.Element} defaults to this.el
4415 tooltipEl : function()
4417 return this.el.select('' + this.tagtype + '', true).first();
4420 scrollToElement : function(e)
4422 var c = document.body;
4425 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4427 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4428 c = document.documentElement;
4431 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4437 var o = target.calcOffsetsTo(c);
4444 this.fireEvent('scrollto', this, options, e);
4446 Roo.get(c).scrollTo('top', options.value, true);
4459 * <span> icon </span>
4460 * <span> text </span>
4461 * <span>badge </span>
4465 * @class Roo.bootstrap.NavSidebarItem
4466 * @extends Roo.bootstrap.NavItem
4467 * Bootstrap Navbar.NavSidebarItem class
4468 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4469 * {bool} open is the menu open
4471 * Create a new Navbar Button
4472 * @param {Object} config The config object
4474 Roo.bootstrap.NavSidebarItem = function(config){
4475 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4480 * The raw click event for the entire grid.
4481 * @param {Roo.EventObject} e
4486 * Fires when the active item active state changes
4487 * @param {Roo.bootstrap.NavSidebarItem} this
4488 * @param {boolean} state the new state
4496 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4498 badgeWeight : 'default',
4502 getAutoCreate : function(){
4507 href : this.href || '#',
4519 html : this.html || ''
4524 cfg.cls += ' active';
4527 if (this.disabled) {
4528 cfg.cls += ' disabled';
4531 cfg.cls += ' open x-open';
4534 if (this.glyphicon || this.icon) {
4535 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4536 a.cn.push({ tag : 'i', cls : c }) ;
4541 if (this.badge !== '') {
4543 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4547 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4548 a.cls += 'dropdown-toggle treeview' ;
4556 initEvents : function()
4558 if (typeof (this.menu) != 'undefined') {
4559 this.menu.parentType = this.xtype;
4560 this.menu.triggerEl = this.el;
4561 this.menu = this.addxtype(Roo.apply({}, this.menu));
4564 this.el.on('click', this.onClick, this);
4567 if(this.badge !== ''){
4569 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4574 onClick : function(e)
4581 if(this.preventDefault){
4585 this.fireEvent('click', this);
4588 disable : function()
4590 this.setDisabled(true);
4595 this.setDisabled(false);
4598 setDisabled : function(state)
4600 if(this.disabled == state){
4604 this.disabled = state;
4607 this.el.addClass('disabled');
4611 this.el.removeClass('disabled');
4616 setActive : function(state)
4618 if(this.active == state){
4622 this.active = state;
4625 this.el.addClass('active');
4629 this.el.removeClass('active');
4634 isActive: function ()
4639 setBadge : function(str)
4645 this.badgeEl.dom.innerHTML = str;
4662 * @class Roo.bootstrap.Row
4663 * @extends Roo.bootstrap.Component
4664 * Bootstrap Row class (contains columns...)
4668 * @param {Object} config The config object
4671 Roo.bootstrap.Row = function(config){
4672 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4675 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4677 getAutoCreate : function(){
4696 * @class Roo.bootstrap.Element
4697 * @extends Roo.bootstrap.Component
4698 * Bootstrap Element class
4699 * @cfg {String} html contents of the element
4700 * @cfg {String} tag tag of the element
4701 * @cfg {String} cls class of the element
4702 * @cfg {Boolean} preventDefault (true|false) default false
4703 * @cfg {Boolean} clickable (true|false) default false
4706 * Create a new Element
4707 * @param {Object} config The config object
4710 Roo.bootstrap.Element = function(config){
4711 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4717 * When a element is chick
4718 * @param {Roo.bootstrap.Element} this
4719 * @param {Roo.EventObject} e
4725 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4730 preventDefault: false,
4733 getAutoCreate : function(){
4744 initEvents: function()
4746 Roo.bootstrap.Element.superclass.initEvents.call(this);
4749 this.el.on('click', this.onClick, this);
4754 onClick : function(e)
4756 if(this.preventDefault){
4760 this.fireEvent('click', this, e);
4763 getValue : function()
4765 return this.el.dom.innerHTML;
4768 setValue : function(value)
4770 this.el.dom.innerHTML = value;
4785 * @class Roo.bootstrap.Pagination
4786 * @extends Roo.bootstrap.Component
4787 * Bootstrap Pagination class
4788 * @cfg {String} size xs | sm | md | lg
4789 * @cfg {Boolean} inverse false | true
4792 * Create a new Pagination
4793 * @param {Object} config The config object
4796 Roo.bootstrap.Pagination = function(config){
4797 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4800 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4806 getAutoCreate : function(){
4812 cfg.cls += ' inverse';
4818 cfg.cls += " " + this.cls;
4836 * @class Roo.bootstrap.PaginationItem
4837 * @extends Roo.bootstrap.Component
4838 * Bootstrap PaginationItem class
4839 * @cfg {String} html text
4840 * @cfg {String} href the link
4841 * @cfg {Boolean} preventDefault (true | false) default true
4842 * @cfg {Boolean} active (true | false) default false
4843 * @cfg {Boolean} disabled default false
4847 * Create a new PaginationItem
4848 * @param {Object} config The config object
4852 Roo.bootstrap.PaginationItem = function(config){
4853 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4858 * The raw click event for the entire grid.
4859 * @param {Roo.EventObject} e
4865 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4869 preventDefault: true,
4874 getAutoCreate : function(){
4880 href : this.href ? this.href : '#',
4881 html : this.html ? this.html : ''
4891 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4895 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4901 initEvents: function() {
4903 this.el.on('click', this.onClick, this);
4906 onClick : function(e)
4908 Roo.log('PaginationItem on click ');
4909 if(this.preventDefault){
4917 this.fireEvent('click', this, e);
4933 * @class Roo.bootstrap.Slider
4934 * @extends Roo.bootstrap.Component
4935 * Bootstrap Slider class
4938 * Create a new Slider
4939 * @param {Object} config The config object
4942 Roo.bootstrap.Slider = function(config){
4943 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4946 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4948 getAutoCreate : function(){
4952 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4956 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4968 * Ext JS Library 1.1.1
4969 * Copyright(c) 2006-2007, Ext JS, LLC.
4971 * Originally Released Under LGPL - original licence link has changed is not relivant.
4974 * <script type="text/javascript">
4979 * @class Roo.grid.ColumnModel
4980 * @extends Roo.util.Observable
4981 * This is the default implementation of a ColumnModel used by the Grid. It defines
4982 * the columns in the grid.
4985 var colModel = new Roo.grid.ColumnModel([
4986 {header: "Ticker", width: 60, sortable: true, locked: true},
4987 {header: "Company Name", width: 150, sortable: true},
4988 {header: "Market Cap.", width: 100, sortable: true},
4989 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4990 {header: "Employees", width: 100, sortable: true, resizable: false}
4995 * The config options listed for this class are options which may appear in each
4996 * individual column definition.
4997 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4999 * @param {Object} config An Array of column config objects. See this class's
5000 * config objects for details.
5002 Roo.grid.ColumnModel = function(config){
5004 * The config passed into the constructor
5006 this.config = config;
5009 // if no id, create one
5010 // if the column does not have a dataIndex mapping,
5011 // map it to the order it is in the config
5012 for(var i = 0, len = config.length; i < len; i++){
5014 if(typeof c.dataIndex == "undefined"){
5017 if(typeof c.renderer == "string"){
5018 c.renderer = Roo.util.Format[c.renderer];
5020 if(typeof c.id == "undefined"){
5023 if(c.editor && c.editor.xtype){
5024 c.editor = Roo.factory(c.editor, Roo.grid);
5026 if(c.editor && c.editor.isFormField){
5027 c.editor = new Roo.grid.GridEditor(c.editor);
5029 this.lookup[c.id] = c;
5033 * The width of columns which have no width specified (defaults to 100)
5036 this.defaultWidth = 100;
5039 * Default sortable of columns which have no sortable specified (defaults to false)
5042 this.defaultSortable = false;
5046 * @event widthchange
5047 * Fires when the width of a column changes.
5048 * @param {ColumnModel} this
5049 * @param {Number} columnIndex The column index
5050 * @param {Number} newWidth The new width
5052 "widthchange": true,
5054 * @event headerchange
5055 * Fires when the text of a header changes.
5056 * @param {ColumnModel} this
5057 * @param {Number} columnIndex The column index
5058 * @param {Number} newText The new header text
5060 "headerchange": true,
5062 * @event hiddenchange
5063 * Fires when a column is hidden or "unhidden".
5064 * @param {ColumnModel} this
5065 * @param {Number} columnIndex The column index
5066 * @param {Boolean} hidden true if hidden, false otherwise
5068 "hiddenchange": true,
5070 * @event columnmoved
5071 * Fires when a column is moved.
5072 * @param {ColumnModel} this
5073 * @param {Number} oldIndex
5074 * @param {Number} newIndex
5076 "columnmoved" : true,
5078 * @event columlockchange
5079 * Fires when a column's locked state is changed
5080 * @param {ColumnModel} this
5081 * @param {Number} colIndex
5082 * @param {Boolean} locked true if locked
5084 "columnlockchange" : true
5086 Roo.grid.ColumnModel.superclass.constructor.call(this);
5088 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5090 * @cfg {String} header The header text to display in the Grid view.
5093 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5094 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5095 * specified, the column's index is used as an index into the Record's data Array.
5098 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5099 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5102 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5103 * Defaults to the value of the {@link #defaultSortable} property.
5104 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5107 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5110 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5113 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5116 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5119 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5120 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5121 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5122 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5125 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5128 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5131 * @cfg {String} cursor (Optional)
5134 * @cfg {String} tooltip (Optional)
5137 * @cfg {Number} xs (Optional)
5140 * @cfg {Number} sm (Optional)
5143 * @cfg {Number} md (Optional)
5146 * @cfg {Number} lg (Optional)
5149 * Returns the id of the column at the specified index.
5150 * @param {Number} index The column index
5151 * @return {String} the id
5153 getColumnId : function(index){
5154 return this.config[index].id;
5158 * Returns the column for a specified id.
5159 * @param {String} id The column id
5160 * @return {Object} the column
5162 getColumnById : function(id){
5163 return this.lookup[id];
5168 * Returns the column for a specified dataIndex.
5169 * @param {String} dataIndex The column dataIndex
5170 * @return {Object|Boolean} the column or false if not found
5172 getColumnByDataIndex: function(dataIndex){
5173 var index = this.findColumnIndex(dataIndex);
5174 return index > -1 ? this.config[index] : false;
5178 * Returns the index for a specified column id.
5179 * @param {String} id The column id
5180 * @return {Number} the index, or -1 if not found
5182 getIndexById : function(id){
5183 for(var i = 0, len = this.config.length; i < len; i++){
5184 if(this.config[i].id == id){
5192 * Returns the index for a specified column dataIndex.
5193 * @param {String} dataIndex The column dataIndex
5194 * @return {Number} the index, or -1 if not found
5197 findColumnIndex : function(dataIndex){
5198 for(var i = 0, len = this.config.length; i < len; i++){
5199 if(this.config[i].dataIndex == dataIndex){
5207 moveColumn : function(oldIndex, newIndex){
5208 var c = this.config[oldIndex];
5209 this.config.splice(oldIndex, 1);
5210 this.config.splice(newIndex, 0, c);
5211 this.dataMap = null;
5212 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5215 isLocked : function(colIndex){
5216 return this.config[colIndex].locked === true;
5219 setLocked : function(colIndex, value, suppressEvent){
5220 if(this.isLocked(colIndex) == value){
5223 this.config[colIndex].locked = value;
5225 this.fireEvent("columnlockchange", this, colIndex, value);
5229 getTotalLockedWidth : function(){
5231 for(var i = 0; i < this.config.length; i++){
5232 if(this.isLocked(i) && !this.isHidden(i)){
5233 this.totalWidth += this.getColumnWidth(i);
5239 getLockedCount : function(){
5240 for(var i = 0, len = this.config.length; i < len; i++){
5241 if(!this.isLocked(i)){
5246 return this.config.length;
5250 * Returns the number of columns.
5253 getColumnCount : function(visibleOnly){
5254 if(visibleOnly === true){
5256 for(var i = 0, len = this.config.length; i < len; i++){
5257 if(!this.isHidden(i)){
5263 return this.config.length;
5267 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5268 * @param {Function} fn
5269 * @param {Object} scope (optional)
5270 * @return {Array} result
5272 getColumnsBy : function(fn, scope){
5274 for(var i = 0, len = this.config.length; i < len; i++){
5275 var c = this.config[i];
5276 if(fn.call(scope||this, c, i) === true){
5284 * Returns true if the specified column is sortable.
5285 * @param {Number} col The column index
5288 isSortable : function(col){
5289 if(typeof this.config[col].sortable == "undefined"){
5290 return this.defaultSortable;
5292 return this.config[col].sortable;
5296 * Returns the rendering (formatting) function defined for the column.
5297 * @param {Number} col The column index.
5298 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5300 getRenderer : function(col){
5301 if(!this.config[col].renderer){
5302 return Roo.grid.ColumnModel.defaultRenderer;
5304 return this.config[col].renderer;
5308 * Sets the rendering (formatting) function for a column.
5309 * @param {Number} col The column index
5310 * @param {Function} fn The function to use to process the cell's raw data
5311 * to return HTML markup for the grid view. The render function is called with
5312 * the following parameters:<ul>
5313 * <li>Data value.</li>
5314 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5315 * <li>css A CSS style string to apply to the table cell.</li>
5316 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5317 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5318 * <li>Row index</li>
5319 * <li>Column index</li>
5320 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5322 setRenderer : function(col, fn){
5323 this.config[col].renderer = fn;
5327 * Returns the width for the specified column.
5328 * @param {Number} col The column index
5331 getColumnWidth : function(col){
5332 return this.config[col].width * 1 || this.defaultWidth;
5336 * Sets the width for a column.
5337 * @param {Number} col The column index
5338 * @param {Number} width The new width
5340 setColumnWidth : function(col, width, suppressEvent){
5341 this.config[col].width = width;
5342 this.totalWidth = null;
5344 this.fireEvent("widthchange", this, col, width);
5349 * Returns the total width of all columns.
5350 * @param {Boolean} includeHidden True to include hidden column widths
5353 getTotalWidth : function(includeHidden){
5354 if(!this.totalWidth){
5355 this.totalWidth = 0;
5356 for(var i = 0, len = this.config.length; i < len; i++){
5357 if(includeHidden || !this.isHidden(i)){
5358 this.totalWidth += this.getColumnWidth(i);
5362 return this.totalWidth;
5366 * Returns the header for the specified column.
5367 * @param {Number} col The column index
5370 getColumnHeader : function(col){
5371 return this.config[col].header;
5375 * Sets the header for a column.
5376 * @param {Number} col The column index
5377 * @param {String} header The new header
5379 setColumnHeader : function(col, header){
5380 this.config[col].header = header;
5381 this.fireEvent("headerchange", this, col, header);
5385 * Returns the tooltip for the specified column.
5386 * @param {Number} col The column index
5389 getColumnTooltip : function(col){
5390 return this.config[col].tooltip;
5393 * Sets the tooltip for a column.
5394 * @param {Number} col The column index
5395 * @param {String} tooltip The new tooltip
5397 setColumnTooltip : function(col, tooltip){
5398 this.config[col].tooltip = tooltip;
5402 * Returns the dataIndex for the specified column.
5403 * @param {Number} col The column index
5406 getDataIndex : function(col){
5407 return this.config[col].dataIndex;
5411 * Sets the dataIndex for a column.
5412 * @param {Number} col The column index
5413 * @param {Number} dataIndex The new dataIndex
5415 setDataIndex : function(col, dataIndex){
5416 this.config[col].dataIndex = dataIndex;
5422 * Returns true if the cell is editable.
5423 * @param {Number} colIndex The column index
5424 * @param {Number} rowIndex The row index - this is nto actually used..?
5427 isCellEditable : function(colIndex, rowIndex){
5428 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5432 * Returns the editor defined for the cell/column.
5433 * return false or null to disable editing.
5434 * @param {Number} colIndex The column index
5435 * @param {Number} rowIndex The row index
5438 getCellEditor : function(colIndex, rowIndex){
5439 return this.config[colIndex].editor;
5443 * Sets if a column is editable.
5444 * @param {Number} col The column index
5445 * @param {Boolean} editable True if the column is editable
5447 setEditable : function(col, editable){
5448 this.config[col].editable = editable;
5453 * Returns true if the column is hidden.
5454 * @param {Number} colIndex The column index
5457 isHidden : function(colIndex){
5458 return this.config[colIndex].hidden;
5463 * Returns true if the column width cannot be changed
5465 isFixed : function(colIndex){
5466 return this.config[colIndex].fixed;
5470 * Returns true if the column can be resized
5473 isResizable : function(colIndex){
5474 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5477 * Sets if a column is hidden.
5478 * @param {Number} colIndex The column index
5479 * @param {Boolean} hidden True if the column is hidden
5481 setHidden : function(colIndex, hidden){
5482 this.config[colIndex].hidden = hidden;
5483 this.totalWidth = null;
5484 this.fireEvent("hiddenchange", this, colIndex, hidden);
5488 * Sets the editor for a column.
5489 * @param {Number} col The column index
5490 * @param {Object} editor The editor object
5492 setEditor : function(col, editor){
5493 this.config[col].editor = editor;
5497 Roo.grid.ColumnModel.defaultRenderer = function(value){
5498 if(typeof value == "string" && value.length < 1){
5504 // Alias for backwards compatibility
5505 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5508 * Ext JS Library 1.1.1
5509 * Copyright(c) 2006-2007, Ext JS, LLC.
5511 * Originally Released Under LGPL - original licence link has changed is not relivant.
5514 * <script type="text/javascript">
5518 * @class Roo.LoadMask
5519 * A simple utility class for generically masking elements while loading data. If the element being masked has
5520 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5521 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5522 * element's UpdateManager load indicator and will be destroyed after the initial load.
5524 * Create a new LoadMask
5525 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5526 * @param {Object} config The config object
5528 Roo.LoadMask = function(el, config){
5529 this.el = Roo.get(el);
5530 Roo.apply(this, config);
5532 this.store.on('beforeload', this.onBeforeLoad, this);
5533 this.store.on('load', this.onLoad, this);
5534 this.store.on('loadexception', this.onLoadException, this);
5535 this.removeMask = false;
5537 var um = this.el.getUpdateManager();
5538 um.showLoadIndicator = false; // disable the default indicator
5539 um.on('beforeupdate', this.onBeforeLoad, this);
5540 um.on('update', this.onLoad, this);
5541 um.on('failure', this.onLoad, this);
5542 this.removeMask = true;
5546 Roo.LoadMask.prototype = {
5548 * @cfg {Boolean} removeMask
5549 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5550 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5554 * The text to display in a centered loading message box (defaults to 'Loading...')
5558 * @cfg {String} msgCls
5559 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5561 msgCls : 'x-mask-loading',
5564 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5570 * Disables the mask to prevent it from being displayed
5572 disable : function(){
5573 this.disabled = true;
5577 * Enables the mask so that it can be displayed
5579 enable : function(){
5580 this.disabled = false;
5583 onLoadException : function()
5587 if (typeof(arguments[3]) != 'undefined') {
5588 Roo.MessageBox.alert("Error loading",arguments[3]);
5592 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5593 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5602 this.el.unmask(this.removeMask);
5607 this.el.unmask(this.removeMask);
5611 onBeforeLoad : function(){
5613 this.el.mask(this.msg, this.msgCls);
5618 destroy : function(){
5620 this.store.un('beforeload', this.onBeforeLoad, this);
5621 this.store.un('load', this.onLoad, this);
5622 this.store.un('loadexception', this.onLoadException, this);
5624 var um = this.el.getUpdateManager();
5625 um.un('beforeupdate', this.onBeforeLoad, this);
5626 um.un('update', this.onLoad, this);
5627 um.un('failure', this.onLoad, this);
5638 * @class Roo.bootstrap.Table
5639 * @extends Roo.bootstrap.Component
5640 * Bootstrap Table class
5641 * @cfg {String} cls table class
5642 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5643 * @cfg {String} bgcolor Specifies the background color for a table
5644 * @cfg {Number} border Specifies whether the table cells should have borders or not
5645 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5646 * @cfg {Number} cellspacing Specifies the space between cells
5647 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5648 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5649 * @cfg {String} sortable Specifies that the table should be sortable
5650 * @cfg {String} summary Specifies a summary of the content of a table
5651 * @cfg {Number} width Specifies the width of a table
5652 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5654 * @cfg {boolean} striped Should the rows be alternative striped
5655 * @cfg {boolean} bordered Add borders to the table
5656 * @cfg {boolean} hover Add hover highlighting
5657 * @cfg {boolean} condensed Format condensed
5658 * @cfg {boolean} responsive Format condensed
5659 * @cfg {Boolean} loadMask (true|false) default false
5660 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5661 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5662 * @cfg {Boolean} rowSelection (true|false) default false
5663 * @cfg {Boolean} cellSelection (true|false) default false
5664 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5668 * Create a new Table
5669 * @param {Object} config The config object
5672 Roo.bootstrap.Table = function(config){
5673 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5675 if (config.container) {
5676 // ctor'ed from a Border/panel.grid
5677 this.container = Roo.get(config.container);
5678 this.container.update("");
5679 this.container.setStyle("overflow", "hidden");
5680 this.container.addClass('x-grid-container');
5685 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5686 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5687 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5688 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5692 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5693 this.sm = this.selModel;
5694 this.sm.xmodule = this.xmodule || false;
5696 if (this.cm && typeof(this.cm.config) == 'undefined') {
5697 this.colModel = new Roo.grid.ColumnModel(this.cm);
5698 this.cm = this.colModel;
5699 this.cm.xmodule = this.xmodule || false;
5702 this.store= Roo.factory(this.store, Roo.data);
5703 this.ds = this.store;
5704 this.ds.xmodule = this.xmodule || false;
5707 if (this.footer && this.store) {
5708 this.footer.dataSource = this.ds;
5709 this.footer = Roo.factory(this.footer);
5716 * Fires when a cell is clicked
5717 * @param {Roo.bootstrap.Table} this
5718 * @param {Roo.Element} el
5719 * @param {Number} rowIndex
5720 * @param {Number} columnIndex
5721 * @param {Roo.EventObject} e
5725 * @event celldblclick
5726 * Fires when a cell is double clicked
5727 * @param {Roo.bootstrap.Table} this
5728 * @param {Roo.Element} el
5729 * @param {Number} rowIndex
5730 * @param {Number} columnIndex
5731 * @param {Roo.EventObject} e
5733 "celldblclick" : true,
5736 * Fires when a row is clicked
5737 * @param {Roo.bootstrap.Table} this
5738 * @param {Roo.Element} el
5739 * @param {Number} rowIndex
5740 * @param {Roo.EventObject} e
5744 * @event rowdblclick
5745 * Fires when a row is double clicked
5746 * @param {Roo.bootstrap.Table} this
5747 * @param {Roo.Element} el
5748 * @param {Number} rowIndex
5749 * @param {Roo.EventObject} e
5751 "rowdblclick" : true,
5754 * Fires when a mouseover occur
5755 * @param {Roo.bootstrap.Table} this
5756 * @param {Roo.Element} el
5757 * @param {Number} rowIndex
5758 * @param {Number} columnIndex
5759 * @param {Roo.EventObject} e
5764 * Fires when a mouseout occur
5765 * @param {Roo.bootstrap.Table} this
5766 * @param {Roo.Element} el
5767 * @param {Number} rowIndex
5768 * @param {Number} columnIndex
5769 * @param {Roo.EventObject} e
5774 * Fires when a row is rendered, so you can change add a style to it.
5775 * @param {Roo.bootstrap.Table} this
5776 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5780 * @event rowsrendered
5781 * Fires when all the rows have been rendered
5782 * @param {Roo.bootstrap.Table} this
5784 'rowsrendered' : true,
5786 * @event contextmenu
5787 * The raw contextmenu event for the entire grid.
5788 * @param {Roo.EventObject} e
5790 "contextmenu" : true,
5792 * @event rowcontextmenu
5793 * Fires when a row is right clicked
5794 * @param {Roo.bootstrap.Table} this
5795 * @param {Number} rowIndex
5796 * @param {Roo.EventObject} e
5798 "rowcontextmenu" : true,
5800 * @event cellcontextmenu
5801 * Fires when a cell is right clicked
5802 * @param {Roo.bootstrap.Table} this
5803 * @param {Number} rowIndex
5804 * @param {Number} cellIndex
5805 * @param {Roo.EventObject} e
5807 "cellcontextmenu" : true,
5809 * @event headercontextmenu
5810 * Fires when a header is right clicked
5811 * @param {Roo.bootstrap.Table} this
5812 * @param {Number} columnIndex
5813 * @param {Roo.EventObject} e
5815 "headercontextmenu" : true
5819 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5844 rowSelection : false,
5845 cellSelection : false,
5848 // Roo.Element - the tbody
5852 container: false, // used by gridpanel...
5854 getAutoCreate : function()
5856 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5860 cls : 'table table-body-fixed',
5865 cfg.cls += ' table-striped';
5869 cfg.cls += ' table-hover';
5871 if (this.bordered) {
5872 cfg.cls += ' table-bordered';
5874 if (this.condensed) {
5875 cfg.cls += ' table-condensed';
5877 if (this.responsive) {
5878 cfg.cls += ' table-responsive';
5882 cfg.cls+= ' ' +this.cls;
5885 // this lot should be simplifed...
5888 cfg.align=this.align;
5891 cfg.bgcolor=this.bgcolor;
5894 cfg.border=this.border;
5896 if (this.cellpadding) {
5897 cfg.cellpadding=this.cellpadding;
5899 if (this.cellspacing) {
5900 cfg.cellspacing=this.cellspacing;
5903 cfg.frame=this.frame;
5906 cfg.rules=this.rules;
5908 if (this.sortable) {
5909 cfg.sortable=this.sortable;
5912 cfg.summary=this.summary;
5915 cfg.width=this.width;
5918 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5921 if(this.store || this.cm){
5922 if(this.headerShow){
5923 cfg.cn.push(this.renderHeader());
5926 cfg.cn.push(this.renderBody());
5928 if(this.footerShow){
5929 cfg.cn.push(this.renderFooter());
5931 // where does this come from?
5932 //cfg.cls+= ' TableGrid';
5935 return { cn : [ cfg ] };
5938 initEvents : function()
5940 if(!this.store || !this.cm){
5944 //Roo.log('initEvents with ds!!!!');
5946 this.mainBody = this.el.select('tbody', true).first();
5951 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5952 e.on('click', _this.sort, _this);
5955 this.el.on("click", this.onClick, this);
5956 this.el.on("dblclick", this.onDblClick, this);
5958 // why is this done????? = it breaks dialogs??
5959 //this.parent().el.setStyle('position', 'relative');
5963 this.footer.parentId = this.id;
5964 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5967 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5969 this.store.on('load', this.onLoad, this);
5970 this.store.on('beforeload', this.onBeforeLoad, this);
5971 this.store.on('update', this.onUpdate, this);
5972 this.store.on('add', this.onAdd, this);
5974 this.el.on("contextmenu", this.onContextMenu, this);
5980 onContextMenu : function(e, t)
5982 this.processEvent("contextmenu", e);
5985 processEvent : function(name, e)
5987 if (name != 'touchstart' ) {
5988 this.fireEvent(name, e);
5991 var t = e.getTarget();
5993 var cell = Roo.get(t);
5999 if(cell.findParent('tfoot', false, true)){
6003 if(cell.findParent('thead', false, true)){
6005 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6006 cell = Roo.get(t).findParent('th', false, true);
6008 Roo.log("failed to find th in thead?");
6009 Roo.log(e.getTarget());
6014 var cellIndex = cell.dom.cellIndex;
6016 var ename = name == 'touchstart' ? 'click' : name;
6017 this.fireEvent("header" + ename, this, cellIndex, e);
6022 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6023 cell = Roo.get(t).findParent('td', false, true);
6026 var row = cell.findParent('tr', false, true);
6027 var cellIndex = cell.dom.cellIndex;
6028 var rowIndex = row.dom.rowIndex - 1;
6032 this.fireEvent("row" + name, this, rowIndex, e);
6036 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6042 onMouseover : function(e, el)
6044 var cell = Roo.get(el);
6050 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6051 cell = cell.findParent('td', false, true);
6054 var row = cell.findParent('tr', false, true);
6055 var cellIndex = cell.dom.cellIndex;
6056 var rowIndex = row.dom.rowIndex - 1; // start from 0
6058 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6062 onMouseout : function(e, el)
6064 var cell = Roo.get(el);
6070 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6071 cell = cell.findParent('td', false, true);
6074 var row = cell.findParent('tr', false, true);
6075 var cellIndex = cell.dom.cellIndex;
6076 var rowIndex = row.dom.rowIndex - 1; // start from 0
6078 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6082 onClick : function(e, el)
6084 var cell = Roo.get(el);
6086 if(!cell || (!this.cellSelection && !this.rowSelection)){
6090 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6091 cell = cell.findParent('td', false, true);
6094 if(!cell || typeof(cell) == 'undefined'){
6098 var row = cell.findParent('tr', false, true);
6100 if(!row || typeof(row) == 'undefined'){
6104 var cellIndex = cell.dom.cellIndex;
6105 var rowIndex = this.getRowIndex(row);
6107 // why??? - should these not be based on SelectionModel?
6108 if(this.cellSelection){
6109 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6112 if(this.rowSelection){
6113 this.fireEvent('rowclick', this, row, rowIndex, e);
6119 onDblClick : function(e,el)
6121 var cell = Roo.get(el);
6123 if(!cell || (!this.CellSelection && !this.RowSelection)){
6127 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6128 cell = cell.findParent('td', false, true);
6131 if(!cell || typeof(cell) == 'undefined'){
6135 var row = cell.findParent('tr', false, true);
6137 if(!row || typeof(row) == 'undefined'){
6141 var cellIndex = cell.dom.cellIndex;
6142 var rowIndex = this.getRowIndex(row);
6144 if(this.CellSelection){
6145 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6148 if(this.RowSelection){
6149 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6153 sort : function(e,el)
6155 var col = Roo.get(el);
6157 if(!col.hasClass('sortable')){
6161 var sort = col.attr('sort');
6164 if(col.hasClass('glyphicon-arrow-up')){
6168 this.store.sortInfo = {field : sort, direction : dir};
6171 Roo.log("calling footer first");
6172 this.footer.onClick('first');
6175 this.store.load({ params : { start : 0 } });
6179 renderHeader : function()
6188 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6190 var config = cm.config[i];
6195 html: cm.getColumnHeader(i)
6200 if(typeof(config.lgHeader) != 'undefined'){
6201 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6204 if(typeof(config.mdHeader) != 'undefined'){
6205 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6208 if(typeof(config.smHeader) != 'undefined'){
6209 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6212 if(typeof(config.xsHeader) != 'undefined'){
6213 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6220 if(typeof(config.tooltip) != 'undefined'){
6221 c.tooltip = config.tooltip;
6224 if(typeof(config.colspan) != 'undefined'){
6225 c.colspan = config.colspan;
6228 if(typeof(config.hidden) != 'undefined' && config.hidden){
6229 c.style += ' display:none;';
6232 if(typeof(config.dataIndex) != 'undefined'){
6233 c.sort = config.dataIndex;
6236 if(typeof(config.sortable) != 'undefined' && config.sortable){
6240 if(typeof(config.align) != 'undefined' && config.align.length){
6241 c.style += ' text-align:' + config.align + ';';
6244 if(typeof(config.width) != 'undefined'){
6245 c.style += ' width:' + config.width + 'px;';
6248 if(typeof(config.cls) != 'undefined'){
6249 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6252 ['xs','sm','md','lg'].map(function(size){
6254 if(typeof(config[size]) == 'undefined'){
6258 if (!config[size]) { // 0 = hidden
6259 c.cls += ' hidden-' + size;
6263 c.cls += ' col-' + size + '-' + config[size];
6273 renderBody : function()
6283 colspan : this.cm.getColumnCount()
6293 renderFooter : function()
6303 colspan : this.cm.getColumnCount()
6317 // Roo.log('ds onload');
6322 var ds = this.store;
6324 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6325 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6326 if (_this.store.sortInfo) {
6328 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6329 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6332 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6333 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6338 var tbody = this.mainBody;
6340 if(ds.getCount() > 0){
6341 ds.data.each(function(d,rowIndex){
6342 var row = this.renderRow(cm, ds, rowIndex);
6344 tbody.createChild(row);
6348 if(row.cellObjects.length){
6349 Roo.each(row.cellObjects, function(r){
6350 _this.renderCellObject(r);
6357 Roo.each(this.el.select('tbody td', true).elements, function(e){
6358 e.on('mouseover', _this.onMouseover, _this);
6361 Roo.each(this.el.select('tbody td', true).elements, function(e){
6362 e.on('mouseout', _this.onMouseout, _this);
6364 this.fireEvent('rowsrendered', this);
6365 //if(this.loadMask){
6366 // this.maskEl.hide();
6373 onUpdate : function(ds,record)
6375 this.refreshRow(record);
6378 onRemove : function(ds, record, index, isUpdate){
6379 if(isUpdate !== true){
6380 this.fireEvent("beforerowremoved", this, index, record);
6382 var bt = this.mainBody.dom;
6384 var rows = this.el.select('tbody > tr', true).elements;
6386 if(typeof(rows[index]) != 'undefined'){
6387 bt.removeChild(rows[index].dom);
6390 // if(bt.rows[index]){
6391 // bt.removeChild(bt.rows[index]);
6394 if(isUpdate !== true){
6395 //this.stripeRows(index);
6396 //this.syncRowHeights(index, index);
6398 this.fireEvent("rowremoved", this, index, record);
6402 onAdd : function(ds, records, rowIndex)
6404 //Roo.log('on Add called');
6405 // - note this does not handle multiple adding very well..
6406 var bt = this.mainBody.dom;
6407 for (var i =0 ; i < records.length;i++) {
6408 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6409 //Roo.log(records[i]);
6410 //Roo.log(this.store.getAt(rowIndex+i));
6411 this.insertRow(this.store, rowIndex + i, false);
6418 refreshRow : function(record){
6419 var ds = this.store, index;
6420 if(typeof record == 'number'){
6422 record = ds.getAt(index);
6424 index = ds.indexOf(record);
6426 this.insertRow(ds, index, true);
6427 this.onRemove(ds, record, index+1, true);
6428 //this.syncRowHeights(index, index);
6430 this.fireEvent("rowupdated", this, index, record);
6433 insertRow : function(dm, rowIndex, isUpdate){
6436 this.fireEvent("beforerowsinserted", this, rowIndex);
6438 //var s = this.getScrollState();
6439 var row = this.renderRow(this.cm, this.store, rowIndex);
6440 // insert before rowIndex..
6441 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6445 if(row.cellObjects.length){
6446 Roo.each(row.cellObjects, function(r){
6447 _this.renderCellObject(r);
6452 this.fireEvent("rowsinserted", this, rowIndex);
6453 //this.syncRowHeights(firstRow, lastRow);
6454 //this.stripeRows(firstRow);
6461 getRowDom : function(rowIndex)
6463 var rows = this.el.select('tbody > tr', true).elements;
6465 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6468 // returns the object tree for a tr..
6471 renderRow : function(cm, ds, rowIndex)
6474 var d = ds.getAt(rowIndex);
6481 var cellObjects = [];
6483 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6484 var config = cm.config[i];
6486 var renderer = cm.getRenderer(i);
6490 if(typeof(renderer) !== 'undefined'){
6491 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6493 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6494 // and are rendered into the cells after the row is rendered - using the id for the element.
6496 if(typeof(value) === 'object'){
6506 rowIndex : rowIndex,
6511 this.fireEvent('rowclass', this, rowcfg);
6515 cls : rowcfg.rowClass,
6517 html: (typeof(value) === 'object') ? '' : value
6524 if(typeof(config.colspan) != 'undefined'){
6525 td.colspan = config.colspan;
6528 if(typeof(config.hidden) != 'undefined' && config.hidden){
6529 td.style += ' display:none;';
6532 if(typeof(config.align) != 'undefined' && config.align.length){
6533 td.style += ' text-align:' + config.align + ';';
6536 if(typeof(config.width) != 'undefined'){
6537 td.style += ' width:' + config.width + 'px;';
6540 if(typeof(config.cursor) != 'undefined'){
6541 td.style += ' cursor:' + config.cursor + ';';
6544 if(typeof(config.cls) != 'undefined'){
6545 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6548 ['xs','sm','md','lg'].map(function(size){
6550 if(typeof(config[size]) == 'undefined'){
6554 if (!config[size]) { // 0 = hidden
6555 td.cls += ' hidden-' + size;
6559 td.cls += ' col-' + size + '-' + config[size];
6567 row.cellObjects = cellObjects;
6575 onBeforeLoad : function()
6577 //Roo.log('ds onBeforeLoad');
6581 //if(this.loadMask){
6582 // this.maskEl.show();
6590 this.el.select('tbody', true).first().dom.innerHTML = '';
6593 * Show or hide a row.
6594 * @param {Number} rowIndex to show or hide
6595 * @param {Boolean} state hide
6597 setRowVisibility : function(rowIndex, state)
6599 var bt = this.mainBody.dom;
6601 var rows = this.el.select('tbody > tr', true).elements;
6603 if(typeof(rows[rowIndex]) == 'undefined'){
6606 rows[rowIndex].dom.style.display = state ? '' : 'none';
6610 getSelectionModel : function(){
6612 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6614 return this.selModel;
6617 * Render the Roo.bootstrap object from renderder
6619 renderCellObject : function(r)
6623 var t = r.cfg.render(r.container);
6626 Roo.each(r.cfg.cn, function(c){
6628 container: t.getChildContainer(),
6631 _this.renderCellObject(child);
6636 getRowIndex : function(row)
6640 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6651 * Returns the grid's underlying element = used by panel.Grid
6652 * @return {Element} The element
6654 getGridEl : function(){
6655 return this.container;
6658 * Forces a resize - used by panel.Grid
6659 * @return {Element} The element
6661 autoSize : function(){
6662 var ctr = Roo.get(this.container.dom.parentElement);
6664 var thd = this.getGridEl().select('thead',true).first();
6665 var tbd = this.getGridEl().select('tbody', true).first();
6667 var cw = Math.max(ctr.getWidth(), this.totalWidth);
6669 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6671 this.getGridEl().select('tr',true).setWidth(cw);
6673 return; // we doe not have a view in this design..
6676 if(this.view.adjustForScroll){
6677 this.view.adjustForScroll();
6693 * @class Roo.bootstrap.TableCell
6694 * @extends Roo.bootstrap.Component
6695 * Bootstrap TableCell class
6696 * @cfg {String} html cell contain text
6697 * @cfg {String} cls cell class
6698 * @cfg {String} tag cell tag (td|th) default td
6699 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6700 * @cfg {String} align Aligns the content in a cell
6701 * @cfg {String} axis Categorizes cells
6702 * @cfg {String} bgcolor Specifies the background color of a cell
6703 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6704 * @cfg {Number} colspan Specifies the number of columns a cell should span
6705 * @cfg {String} headers Specifies one or more header cells a cell is related to
6706 * @cfg {Number} height Sets the height of a cell
6707 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6708 * @cfg {Number} rowspan Sets the number of rows a cell should span
6709 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6710 * @cfg {String} valign Vertical aligns the content in a cell
6711 * @cfg {Number} width Specifies the width of a cell
6714 * Create a new TableCell
6715 * @param {Object} config The config object
6718 Roo.bootstrap.TableCell = function(config){
6719 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6722 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6742 getAutoCreate : function(){
6743 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6763 cfg.align=this.align
6769 cfg.bgcolor=this.bgcolor
6772 cfg.charoff=this.charoff
6775 cfg.colspan=this.colspan
6778 cfg.headers=this.headers
6781 cfg.height=this.height
6784 cfg.nowrap=this.nowrap
6787 cfg.rowspan=this.rowspan
6790 cfg.scope=this.scope
6793 cfg.valign=this.valign
6796 cfg.width=this.width
6815 * @class Roo.bootstrap.TableRow
6816 * @extends Roo.bootstrap.Component
6817 * Bootstrap TableRow class
6818 * @cfg {String} cls row class
6819 * @cfg {String} align Aligns the content in a table row
6820 * @cfg {String} bgcolor Specifies a background color for a table row
6821 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6822 * @cfg {String} valign Vertical aligns the content in a table row
6825 * Create a new TableRow
6826 * @param {Object} config The config object
6829 Roo.bootstrap.TableRow = function(config){
6830 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6833 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6841 getAutoCreate : function(){
6842 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6852 cfg.align = this.align;
6855 cfg.bgcolor = this.bgcolor;
6858 cfg.charoff = this.charoff;
6861 cfg.valign = this.valign;
6879 * @class Roo.bootstrap.TableBody
6880 * @extends Roo.bootstrap.Component
6881 * Bootstrap TableBody class
6882 * @cfg {String} cls element class
6883 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6884 * @cfg {String} align Aligns the content inside the element
6885 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6886 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6889 * Create a new TableBody
6890 * @param {Object} config The config object
6893 Roo.bootstrap.TableBody = function(config){
6894 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6897 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6905 getAutoCreate : function(){
6906 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6920 cfg.align = this.align;
6923 cfg.charoff = this.charoff;
6926 cfg.valign = this.valign;
6933 // initEvents : function()
6940 // this.store = Roo.factory(this.store, Roo.data);
6941 // this.store.on('load', this.onLoad, this);
6943 // this.store.load();
6947 // onLoad: function ()
6949 // this.fireEvent('load', this);
6959 * Ext JS Library 1.1.1
6960 * Copyright(c) 2006-2007, Ext JS, LLC.
6962 * Originally Released Under LGPL - original licence link has changed is not relivant.
6965 * <script type="text/javascript">
6968 // as we use this in bootstrap.
6969 Roo.namespace('Roo.form');
6971 * @class Roo.form.Action
6972 * Internal Class used to handle form actions
6974 * @param {Roo.form.BasicForm} el The form element or its id
6975 * @param {Object} config Configuration options
6980 // define the action interface
6981 Roo.form.Action = function(form, options){
6983 this.options = options || {};
6986 * Client Validation Failed
6989 Roo.form.Action.CLIENT_INVALID = 'client';
6991 * Server Validation Failed
6994 Roo.form.Action.SERVER_INVALID = 'server';
6996 * Connect to Server Failed
6999 Roo.form.Action.CONNECT_FAILURE = 'connect';
7001 * Reading Data from Server Failed
7004 Roo.form.Action.LOAD_FAILURE = 'load';
7006 Roo.form.Action.prototype = {
7008 failureType : undefined,
7009 response : undefined,
7013 run : function(options){
7018 success : function(response){
7023 handleResponse : function(response){
7027 // default connection failure
7028 failure : function(response){
7030 this.response = response;
7031 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7032 this.form.afterAction(this, false);
7035 processResponse : function(response){
7036 this.response = response;
7037 if(!response.responseText){
7040 this.result = this.handleResponse(response);
7044 // utility functions used internally
7045 getUrl : function(appendParams){
7046 var url = this.options.url || this.form.url || this.form.el.dom.action;
7048 var p = this.getParams();
7050 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7056 getMethod : function(){
7057 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7060 getParams : function(){
7061 var bp = this.form.baseParams;
7062 var p = this.options.params;
7064 if(typeof p == "object"){
7065 p = Roo.urlEncode(Roo.applyIf(p, bp));
7066 }else if(typeof p == 'string' && bp){
7067 p += '&' + Roo.urlEncode(bp);
7070 p = Roo.urlEncode(bp);
7075 createCallback : function(){
7077 success: this.success,
7078 failure: this.failure,
7080 timeout: (this.form.timeout*1000),
7081 upload: this.form.fileUpload ? this.success : undefined
7086 Roo.form.Action.Submit = function(form, options){
7087 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7090 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7093 haveProgress : false,
7094 uploadComplete : false,
7096 // uploadProgress indicator.
7097 uploadProgress : function()
7099 if (!this.form.progressUrl) {
7103 if (!this.haveProgress) {
7104 Roo.MessageBox.progress("Uploading", "Uploading");
7106 if (this.uploadComplete) {
7107 Roo.MessageBox.hide();
7111 this.haveProgress = true;
7113 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7115 var c = new Roo.data.Connection();
7117 url : this.form.progressUrl,
7122 success : function(req){
7123 //console.log(data);
7127 rdata = Roo.decode(req.responseText)
7129 Roo.log("Invalid data from server..");
7133 if (!rdata || !rdata.success) {
7135 Roo.MessageBox.alert(Roo.encode(rdata));
7138 var data = rdata.data;
7140 if (this.uploadComplete) {
7141 Roo.MessageBox.hide();
7146 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7147 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7150 this.uploadProgress.defer(2000,this);
7153 failure: function(data) {
7154 Roo.log('progress url failed ');
7165 // run get Values on the form, so it syncs any secondary forms.
7166 this.form.getValues();
7168 var o = this.options;
7169 var method = this.getMethod();
7170 var isPost = method == 'POST';
7171 if(o.clientValidation === false || this.form.isValid()){
7173 if (this.form.progressUrl) {
7174 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7175 (new Date() * 1) + '' + Math.random());
7180 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7181 form:this.form.el.dom,
7182 url:this.getUrl(!isPost),
7184 params:isPost ? this.getParams() : null,
7185 isUpload: this.form.fileUpload
7188 this.uploadProgress();
7190 }else if (o.clientValidation !== false){ // client validation failed
7191 this.failureType = Roo.form.Action.CLIENT_INVALID;
7192 this.form.afterAction(this, false);
7196 success : function(response)
7198 this.uploadComplete= true;
7199 if (this.haveProgress) {
7200 Roo.MessageBox.hide();
7204 var result = this.processResponse(response);
7205 if(result === true || result.success){
7206 this.form.afterAction(this, true);
7210 this.form.markInvalid(result.errors);
7211 this.failureType = Roo.form.Action.SERVER_INVALID;
7213 this.form.afterAction(this, false);
7215 failure : function(response)
7217 this.uploadComplete= true;
7218 if (this.haveProgress) {
7219 Roo.MessageBox.hide();
7222 this.response = response;
7223 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7224 this.form.afterAction(this, false);
7227 handleResponse : function(response){
7228 if(this.form.errorReader){
7229 var rs = this.form.errorReader.read(response);
7232 for(var i = 0, len = rs.records.length; i < len; i++) {
7233 var r = rs.records[i];
7237 if(errors.length < 1){
7241 success : rs.success,
7247 ret = Roo.decode(response.responseText);
7251 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7261 Roo.form.Action.Load = function(form, options){
7262 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7263 this.reader = this.form.reader;
7266 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7271 Roo.Ajax.request(Roo.apply(
7272 this.createCallback(), {
7273 method:this.getMethod(),
7274 url:this.getUrl(false),
7275 params:this.getParams()
7279 success : function(response){
7281 var result = this.processResponse(response);
7282 if(result === true || !result.success || !result.data){
7283 this.failureType = Roo.form.Action.LOAD_FAILURE;
7284 this.form.afterAction(this, false);
7287 this.form.clearInvalid();
7288 this.form.setValues(result.data);
7289 this.form.afterAction(this, true);
7292 handleResponse : function(response){
7293 if(this.form.reader){
7294 var rs = this.form.reader.read(response);
7295 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7297 success : rs.success,
7301 return Roo.decode(response.responseText);
7305 Roo.form.Action.ACTION_TYPES = {
7306 'load' : Roo.form.Action.Load,
7307 'submit' : Roo.form.Action.Submit
7316 * @class Roo.bootstrap.Form
7317 * @extends Roo.bootstrap.Component
7318 * Bootstrap Form class
7319 * @cfg {String} method GET | POST (default POST)
7320 * @cfg {String} labelAlign top | left (default top)
7321 * @cfg {String} align left | right - for navbars
7322 * @cfg {Boolean} loadMask load mask when submit (default true)
7327 * @param {Object} config The config object
7331 Roo.bootstrap.Form = function(config){
7332 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7335 * @event clientvalidation
7336 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7337 * @param {Form} this
7338 * @param {Boolean} valid true if the form has passed client-side validation
7340 clientvalidation: true,
7342 * @event beforeaction
7343 * Fires before any action is performed. Return false to cancel the action.
7344 * @param {Form} this
7345 * @param {Action} action The action to be performed
7349 * @event actionfailed
7350 * Fires when an action fails.
7351 * @param {Form} this
7352 * @param {Action} action The action that failed
7354 actionfailed : true,
7356 * @event actioncomplete
7357 * Fires when an action is completed.
7358 * @param {Form} this
7359 * @param {Action} action The action that completed
7361 actioncomplete : true
7366 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7369 * @cfg {String} method
7370 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7375 * The URL to use for form actions if one isn't supplied in the action options.
7378 * @cfg {Boolean} fileUpload
7379 * Set to true if this form is a file upload.
7383 * @cfg {Object} baseParams
7384 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7388 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7392 * @cfg {Sting} align (left|right) for navbar forms
7397 activeAction : null,
7400 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7401 * element by passing it or its id or mask the form itself by passing in true.
7404 waitMsgTarget : false,
7408 getAutoCreate : function(){
7412 method : this.method || 'POST',
7413 id : this.id || Roo.id(),
7416 if (this.parent().xtype.match(/^Nav/)) {
7417 cfg.cls = 'navbar-form navbar-' + this.align;
7421 if (this.labelAlign == 'left' ) {
7422 cfg.cls += ' form-horizontal';
7428 initEvents : function()
7430 this.el.on('submit', this.onSubmit, this);
7431 // this was added as random key presses on the form where triggering form submit.
7432 this.el.on('keypress', function(e) {
7433 if (e.getCharCode() != 13) {
7436 // we might need to allow it for textareas.. and some other items.
7437 // check e.getTarget().
7439 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7443 Roo.log("keypress blocked");
7451 onSubmit : function(e){
7456 * Returns true if client-side validation on the form is successful.
7459 isValid : function(){
7460 var items = this.getItems();
7462 items.each(function(f){
7471 * Returns true if any fields in this form have changed since their original load.
7474 isDirty : function(){
7476 var items = this.getItems();
7477 items.each(function(f){
7487 * Performs a predefined action (submit or load) or custom actions you define on this form.
7488 * @param {String} actionName The name of the action type
7489 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7490 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7491 * accept other config options):
7493 Property Type Description
7494 ---------------- --------------- ----------------------------------------------------------------------------------
7495 url String The url for the action (defaults to the form's url)
7496 method String The form method to use (defaults to the form's method, or POST if not defined)
7497 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7498 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7499 validate the form on the client (defaults to false)
7501 * @return {BasicForm} this
7503 doAction : function(action, options){
7504 if(typeof action == 'string'){
7505 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7507 if(this.fireEvent('beforeaction', this, action) !== false){
7508 this.beforeAction(action);
7509 action.run.defer(100, action);
7515 beforeAction : function(action){
7516 var o = action.options;
7519 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7521 // not really supported yet.. ??
7523 //if(this.waitMsgTarget === true){
7524 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7525 //}else if(this.waitMsgTarget){
7526 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7527 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7529 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7535 afterAction : function(action, success){
7536 this.activeAction = null;
7537 var o = action.options;
7539 //if(this.waitMsgTarget === true){
7541 //}else if(this.waitMsgTarget){
7542 // this.waitMsgTarget.unmask();
7544 // Roo.MessageBox.updateProgress(1);
7545 // Roo.MessageBox.hide();
7552 Roo.callback(o.success, o.scope, [this, action]);
7553 this.fireEvent('actioncomplete', this, action);
7557 // failure condition..
7558 // we have a scenario where updates need confirming.
7559 // eg. if a locking scenario exists..
7560 // we look for { errors : { needs_confirm : true }} in the response.
7562 (typeof(action.result) != 'undefined') &&
7563 (typeof(action.result.errors) != 'undefined') &&
7564 (typeof(action.result.errors.needs_confirm) != 'undefined')
7567 Roo.log("not supported yet");
7570 Roo.MessageBox.confirm(
7571 "Change requires confirmation",
7572 action.result.errorMsg,
7577 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7587 Roo.callback(o.failure, o.scope, [this, action]);
7588 // show an error message if no failed handler is set..
7589 if (!this.hasListener('actionfailed')) {
7590 Roo.log("need to add dialog support");
7592 Roo.MessageBox.alert("Error",
7593 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7594 action.result.errorMsg :
7595 "Saving Failed, please check your entries or try again"
7600 this.fireEvent('actionfailed', this, action);
7605 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7606 * @param {String} id The value to search for
7609 findField : function(id){
7610 var items = this.getItems();
7611 var field = items.get(id);
7613 items.each(function(f){
7614 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7621 return field || null;
7624 * Mark fields in this form invalid in bulk.
7625 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7626 * @return {BasicForm} this
7628 markInvalid : function(errors){
7629 if(errors instanceof Array){
7630 for(var i = 0, len = errors.length; i < len; i++){
7631 var fieldError = errors[i];
7632 var f = this.findField(fieldError.id);
7634 f.markInvalid(fieldError.msg);
7640 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7641 field.markInvalid(errors[id]);
7645 //Roo.each(this.childForms || [], function (f) {
7646 // f.markInvalid(errors);
7653 * Set values for fields in this form in bulk.
7654 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7655 * @return {BasicForm} this
7657 setValues : function(values){
7658 if(values instanceof Array){ // array of objects
7659 for(var i = 0, len = values.length; i < len; i++){
7661 var f = this.findField(v.id);
7663 f.setValue(v.value);
7664 if(this.trackResetOnLoad){
7665 f.originalValue = f.getValue();
7669 }else{ // object hash
7672 if(typeof values[id] != 'function' && (field = this.findField(id))){
7674 if (field.setFromData &&
7676 field.displayField &&
7677 // combos' with local stores can
7678 // be queried via setValue()
7679 // to set their value..
7680 (field.store && !field.store.isLocal)
7684 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7685 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7686 field.setFromData(sd);
7689 field.setValue(values[id]);
7693 if(this.trackResetOnLoad){
7694 field.originalValue = field.getValue();
7700 //Roo.each(this.childForms || [], function (f) {
7701 // f.setValues(values);
7708 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7709 * they are returned as an array.
7710 * @param {Boolean} asString
7713 getValues : function(asString){
7714 //if (this.childForms) {
7715 // copy values from the child forms
7716 // Roo.each(this.childForms, function (f) {
7717 // this.setValues(f.getValues());
7723 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7724 if(asString === true){
7727 return Roo.urlDecode(fs);
7731 * Returns the fields in this form as an object with key/value pairs.
7732 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7735 getFieldValues : function(with_hidden)
7737 var items = this.getItems();
7739 items.each(function(f){
7743 var v = f.getValue();
7744 if (f.inputType =='radio') {
7745 if (typeof(ret[f.getName()]) == 'undefined') {
7746 ret[f.getName()] = ''; // empty..
7749 if (!f.el.dom.checked) {
7757 // not sure if this supported any more..
7758 if ((typeof(v) == 'object') && f.getRawValue) {
7759 v = f.getRawValue() ; // dates..
7761 // combo boxes where name != hiddenName...
7762 if (f.name != f.getName()) {
7763 ret[f.name] = f.getRawValue();
7765 ret[f.getName()] = v;
7772 * Clears all invalid messages in this form.
7773 * @return {BasicForm} this
7775 clearInvalid : function(){
7776 var items = this.getItems();
7778 items.each(function(f){
7789 * @return {BasicForm} this
7792 var items = this.getItems();
7793 items.each(function(f){
7797 Roo.each(this.childForms || [], function (f) {
7804 getItems : function()
7806 var r=new Roo.util.MixedCollection(false, function(o){
7807 return o.id || (o.id = Roo.id());
7809 var iter = function(el) {
7816 Roo.each(el.items,function(e) {
7836 * Ext JS Library 1.1.1
7837 * Copyright(c) 2006-2007, Ext JS, LLC.
7839 * Originally Released Under LGPL - original licence link has changed is not relivant.
7842 * <script type="text/javascript">
7845 * @class Roo.form.VTypes
7846 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7849 Roo.form.VTypes = function(){
7850 // closure these in so they are only created once.
7851 var alpha = /^[a-zA-Z_]+$/;
7852 var alphanum = /^[a-zA-Z0-9_]+$/;
7853 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7854 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7856 // All these messages and functions are configurable
7859 * The function used to validate email addresses
7860 * @param {String} value The email address
7862 'email' : function(v){
7863 return email.test(v);
7866 * The error text to display when the email validation function returns false
7869 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7871 * The keystroke filter mask to be applied on email input
7874 'emailMask' : /[a-z0-9_\.\-@]/i,
7877 * The function used to validate URLs
7878 * @param {String} value The URL
7880 'url' : function(v){
7884 * The error text to display when the url validation function returns false
7887 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7890 * The function used to validate alpha values
7891 * @param {String} value The value
7893 'alpha' : function(v){
7894 return alpha.test(v);
7897 * The error text to display when the alpha validation function returns false
7900 'alphaText' : 'This field should only contain letters and _',
7902 * The keystroke filter mask to be applied on alpha input
7905 'alphaMask' : /[a-z_]/i,
7908 * The function used to validate alphanumeric values
7909 * @param {String} value The value
7911 'alphanum' : function(v){
7912 return alphanum.test(v);
7915 * The error text to display when the alphanumeric validation function returns false
7918 'alphanumText' : 'This field should only contain letters, numbers and _',
7920 * The keystroke filter mask to be applied on alphanumeric input
7923 'alphanumMask' : /[a-z0-9_]/i
7933 * @class Roo.bootstrap.Input
7934 * @extends Roo.bootstrap.Component
7935 * Bootstrap Input class
7936 * @cfg {Boolean} disabled is it disabled
7937 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7938 * @cfg {String} name name of the input
7939 * @cfg {string} fieldLabel - the label associated
7940 * @cfg {string} placeholder - placeholder to put in text.
7941 * @cfg {string} before - input group add on before
7942 * @cfg {string} after - input group add on after
7943 * @cfg {string} size - (lg|sm) or leave empty..
7944 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7945 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7946 * @cfg {Number} md colspan out of 12 for computer-sized screens
7947 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7948 * @cfg {string} value default value of the input
7949 * @cfg {Number} labelWidth set the width of label (0-12)
7950 * @cfg {String} labelAlign (top|left)
7951 * @cfg {Boolean} readOnly Specifies that the field should be read-only
7952 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7954 * @cfg {String} align (left|center|right) Default left
7955 * @cfg {Boolean} forceFeedback (true|false) Default false
7961 * Create a new Input
7962 * @param {Object} config The config object
7965 Roo.bootstrap.Input = function(config){
7966 Roo.bootstrap.Input.superclass.constructor.call(this, config);
7971 * Fires when this field receives input focus.
7972 * @param {Roo.form.Field} this
7977 * Fires when this field loses input focus.
7978 * @param {Roo.form.Field} this
7983 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
7984 * {@link Roo.EventObject#getKey} to determine which key was pressed.
7985 * @param {Roo.form.Field} this
7986 * @param {Roo.EventObject} e The event object
7991 * Fires just before the field blurs if the field value has changed.
7992 * @param {Roo.form.Field} this
7993 * @param {Mixed} newValue The new value
7994 * @param {Mixed} oldValue The original value
7999 * Fires after the field has been marked as invalid.
8000 * @param {Roo.form.Field} this
8001 * @param {String} msg The validation message
8006 * Fires after the field has been validated with no errors.
8007 * @param {Roo.form.Field} this
8012 * Fires after the key up
8013 * @param {Roo.form.Field} this
8014 * @param {Roo.EventObject} e The event Object
8020 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8022 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8023 automatic validation (defaults to "keyup").
8025 validationEvent : "keyup",
8027 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8029 validateOnBlur : true,
8031 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8033 validationDelay : 250,
8035 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8037 focusClass : "x-form-focus", // not needed???
8041 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8043 invalidClass : "has-warning",
8046 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8048 validClass : "has-success",
8051 * @cfg {Boolean} hasFeedback (true|false) default true
8056 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8058 invalidFeedbackClass : "glyphicon-warning-sign",
8061 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8063 validFeedbackClass : "glyphicon-ok",
8066 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8068 selectOnFocus : false,
8071 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8075 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8080 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8082 disableKeyFilter : false,
8085 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8089 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8093 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8095 blankText : "This field is required",
8098 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8102 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8104 maxLength : Number.MAX_VALUE,
8106 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8108 minLengthText : "The minimum length for this field is {0}",
8110 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8112 maxLengthText : "The maximum length for this field is {0}",
8116 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8117 * If available, this function will be called only after the basic validators all return true, and will be passed the
8118 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8122 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8123 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8124 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8128 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8132 autocomplete: false,
8151 formatedValue : false,
8152 forceFeedback : false,
8154 parentLabelAlign : function()
8157 while (parent.parent()) {
8158 parent = parent.parent();
8159 if (typeof(parent.labelAlign) !='undefined') {
8160 return parent.labelAlign;
8167 getAutoCreate : function(){
8169 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8175 if(this.inputType != 'hidden'){
8176 cfg.cls = 'form-group' //input-group
8182 type : this.inputType,
8184 cls : 'form-control',
8185 placeholder : this.placeholder || '',
8186 autocomplete : this.autocomplete || 'new-password'
8191 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8194 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8195 input.maxLength = this.maxLength;
8198 if (this.disabled) {
8199 input.disabled=true;
8202 if (this.readOnly) {
8203 input.readonly=true;
8207 input.name = this.name;
8210 input.cls += ' input-' + this.size;
8213 ['xs','sm','md','lg'].map(function(size){
8214 if (settings[size]) {
8215 cfg.cls += ' col-' + size + '-' + settings[size];
8219 var inputblock = input;
8223 cls: 'glyphicon form-control-feedback'
8226 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8229 cls : 'has-feedback',
8237 if (this.before || this.after) {
8240 cls : 'input-group',
8244 if (this.before && typeof(this.before) == 'string') {
8246 inputblock.cn.push({
8248 cls : 'roo-input-before input-group-addon',
8252 if (this.before && typeof(this.before) == 'object') {
8253 this.before = Roo.factory(this.before);
8255 inputblock.cn.push({
8257 cls : 'roo-input-before input-group-' +
8258 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8262 inputblock.cn.push(input);
8264 if (this.after && typeof(this.after) == 'string') {
8265 inputblock.cn.push({
8267 cls : 'roo-input-after input-group-addon',
8271 if (this.after && typeof(this.after) == 'object') {
8272 this.after = Roo.factory(this.after);
8274 inputblock.cn.push({
8276 cls : 'roo-input-after input-group-' +
8277 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8281 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8282 inputblock.cls += ' has-feedback';
8283 inputblock.cn.push(feedback);
8287 if (align ==='left' && this.fieldLabel.length) {
8294 cls : 'control-label col-sm-' + this.labelWidth,
8295 html : this.fieldLabel
8299 cls : "col-sm-" + (12 - this.labelWidth),
8306 } else if ( this.fieldLabel.length) {
8312 //cls : 'input-group-addon',
8313 html : this.fieldLabel
8332 if (this.parentType === 'Navbar' && this.parent().bar) {
8333 cfg.cls += ' navbar-form';
8340 * return the real input element.
8342 inputEl: function ()
8344 return this.el.select('input.form-control',true).first();
8347 tooltipEl : function()
8349 return this.inputEl();
8352 setDisabled : function(v)
8354 var i = this.inputEl().dom;
8356 i.removeAttribute('disabled');
8360 i.setAttribute('disabled','true');
8362 initEvents : function()
8365 this.inputEl().on("keydown" , this.fireKey, this);
8366 this.inputEl().on("focus", this.onFocus, this);
8367 this.inputEl().on("blur", this.onBlur, this);
8369 this.inputEl().relayEvent('keyup', this);
8371 // reference to original value for reset
8372 this.originalValue = this.getValue();
8373 //Roo.form.TextField.superclass.initEvents.call(this);
8374 if(this.validationEvent == 'keyup'){
8375 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8376 this.inputEl().on('keyup', this.filterValidation, this);
8378 else if(this.validationEvent !== false){
8379 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8382 if(this.selectOnFocus){
8383 this.on("focus", this.preFocus, this);
8386 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8387 this.inputEl().on("keypress", this.filterKeys, this);
8390 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8391 this.el.on("click", this.autoSize, this);
8394 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8395 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8398 if (typeof(this.before) == 'object') {
8399 this.before.render(this.el.select('.roo-input-before',true).first());
8401 if (typeof(this.after) == 'object') {
8402 this.after.render(this.el.select('.roo-input-after',true).first());
8407 filterValidation : function(e){
8408 if(!e.isNavKeyPress()){
8409 this.validationTask.delay(this.validationDelay);
8413 * Validates the field value
8414 * @return {Boolean} True if the value is valid, else false
8416 validate : function(){
8417 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8418 if(this.disabled || this.validateValue(this.getRawValue())){
8429 * Validates a value according to the field's validation rules and marks the field as invalid
8430 * if the validation fails
8431 * @param {Mixed} value The value to validate
8432 * @return {Boolean} True if the value is valid, else false
8434 validateValue : function(value){
8435 if(value.length < 1) { // if it's blank
8436 if(this.allowBlank){
8442 if(value.length < this.minLength){
8445 if(value.length > this.maxLength){
8449 var vt = Roo.form.VTypes;
8450 if(!vt[this.vtype](value, this)){
8454 if(typeof this.validator == "function"){
8455 var msg = this.validator(value);
8461 if(this.regex && !this.regex.test(value)){
8471 fireKey : function(e){
8472 //Roo.log('field ' + e.getKey());
8473 if(e.isNavKeyPress()){
8474 this.fireEvent("specialkey", this, e);
8477 focus : function (selectText){
8479 this.inputEl().focus();
8480 if(selectText === true){
8481 this.inputEl().dom.select();
8487 onFocus : function(){
8488 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8489 // this.el.addClass(this.focusClass);
8492 this.hasFocus = true;
8493 this.startValue = this.getValue();
8494 this.fireEvent("focus", this);
8498 beforeBlur : Roo.emptyFn,
8502 onBlur : function(){
8504 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8505 //this.el.removeClass(this.focusClass);
8507 this.hasFocus = false;
8508 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8511 var v = this.getValue();
8512 if(String(v) !== String(this.startValue)){
8513 this.fireEvent('change', this, v, this.startValue);
8515 this.fireEvent("blur", this);
8519 * Resets the current field value to the originally loaded value and clears any validation messages
8522 this.setValue(this.originalValue);
8526 * Returns the name of the field
8527 * @return {Mixed} name The name field
8529 getName: function(){
8533 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8534 * @return {Mixed} value The field value
8536 getValue : function(){
8538 var v = this.inputEl().getValue();
8543 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8544 * @return {Mixed} value The field value
8546 getRawValue : function(){
8547 var v = this.inputEl().getValue();
8553 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8554 * @param {Mixed} value The value to set
8556 setRawValue : function(v){
8557 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8560 selectText : function(start, end){
8561 var v = this.getRawValue();
8563 start = start === undefined ? 0 : start;
8564 end = end === undefined ? v.length : end;
8565 var d = this.inputEl().dom;
8566 if(d.setSelectionRange){
8567 d.setSelectionRange(start, end);
8568 }else if(d.createTextRange){
8569 var range = d.createTextRange();
8570 range.moveStart("character", start);
8571 range.moveEnd("character", v.length-end);
8578 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8579 * @param {Mixed} value The value to set
8581 setValue : function(v){
8584 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8590 processValue : function(value){
8591 if(this.stripCharsRe){
8592 var newValue = value.replace(this.stripCharsRe, '');
8593 if(newValue !== value){
8594 this.setRawValue(newValue);
8601 preFocus : function(){
8603 if(this.selectOnFocus){
8604 this.inputEl().dom.select();
8607 filterKeys : function(e){
8609 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8612 var c = e.getCharCode(), cc = String.fromCharCode(c);
8613 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8616 if(!this.maskRe.test(cc)){
8621 * Clear any invalid styles/messages for this field
8623 clearInvalid : function(){
8625 if(!this.el || this.preventMark){ // not rendered
8629 var label = this.el.select('label', true).first();
8630 var icon = this.el.select('i.fa-star', true).first();
8636 this.el.removeClass(this.invalidClass);
8638 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8640 var feedback = this.el.select('.form-control-feedback', true).first();
8643 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8648 this.fireEvent('valid', this);
8652 * Mark this field as valid
8654 markValid : function()
8656 if(!this.el || this.preventMark){ // not rendered
8660 this.el.removeClass([this.invalidClass, this.validClass]);
8662 var feedback = this.el.select('.form-control-feedback', true).first();
8665 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8668 if(this.disabled || this.allowBlank){
8672 var formGroup = this.el.findParent('.form-group', false, true);
8676 var label = formGroup.select('label', true).first();
8677 var icon = formGroup.select('i.fa-star', true).first();
8684 this.el.addClass(this.validClass);
8686 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8688 var feedback = this.el.select('.form-control-feedback', true).first();
8691 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8692 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8697 this.fireEvent('valid', this);
8701 * Mark this field as invalid
8702 * @param {String} msg The validation message
8704 markInvalid : function(msg)
8706 if(!this.el || this.preventMark){ // not rendered
8710 this.el.removeClass([this.invalidClass, this.validClass]);
8712 var feedback = this.el.select('.form-control-feedback', true).first();
8715 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8718 if(this.disabled || this.allowBlank){
8722 var formGroup = this.el.findParent('.form-group', false, true);
8725 var label = formGroup.select('label', true).first();
8726 var icon = formGroup.select('i.fa-star', true).first();
8728 if(!this.getValue().length && label && !icon){
8729 this.el.findParent('.form-group', false, true).createChild({
8731 cls : 'text-danger fa fa-lg fa-star',
8732 tooltip : 'This field is required',
8733 style : 'margin-right:5px;'
8739 this.el.addClass(this.invalidClass);
8741 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8743 var feedback = this.el.select('.form-control-feedback', true).first();
8746 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8748 if(this.getValue().length || this.forceFeedback){
8749 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8756 this.fireEvent('invalid', this, msg);
8759 SafariOnKeyDown : function(event)
8761 // this is a workaround for a password hang bug on chrome/ webkit.
8763 var isSelectAll = false;
8765 if(this.inputEl().dom.selectionEnd > 0){
8766 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8768 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8769 event.preventDefault();
8774 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8776 event.preventDefault();
8777 // this is very hacky as keydown always get's upper case.
8779 var cc = String.fromCharCode(event.getCharCode());
8780 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8784 adjustWidth : function(tag, w){
8785 tag = tag.toLowerCase();
8786 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8787 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8791 if(tag == 'textarea'){
8794 }else if(Roo.isOpera){
8798 if(tag == 'textarea'){
8817 * @class Roo.bootstrap.TextArea
8818 * @extends Roo.bootstrap.Input
8819 * Bootstrap TextArea class
8820 * @cfg {Number} cols Specifies the visible width of a text area
8821 * @cfg {Number} rows Specifies the visible number of lines in a text area
8822 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8823 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8824 * @cfg {string} html text
8827 * Create a new TextArea
8828 * @param {Object} config The config object
8831 Roo.bootstrap.TextArea = function(config){
8832 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8836 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8846 getAutoCreate : function(){
8848 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8859 value : this.value || '',
8860 html: this.html || '',
8861 cls : 'form-control',
8862 placeholder : this.placeholder || ''
8866 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8867 input.maxLength = this.maxLength;
8871 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8875 input.cols = this.cols;
8878 if (this.readOnly) {
8879 input.readonly = true;
8883 input.name = this.name;
8887 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8891 ['xs','sm','md','lg'].map(function(size){
8892 if (settings[size]) {
8893 cfg.cls += ' col-' + size + '-' + settings[size];
8897 var inputblock = input;
8899 if(this.hasFeedback && !this.allowBlank){
8903 cls: 'glyphicon form-control-feedback'
8907 cls : 'has-feedback',
8916 if (this.before || this.after) {
8919 cls : 'input-group',
8923 inputblock.cn.push({
8925 cls : 'input-group-addon',
8930 inputblock.cn.push(input);
8932 if(this.hasFeedback && !this.allowBlank){
8933 inputblock.cls += ' has-feedback';
8934 inputblock.cn.push(feedback);
8938 inputblock.cn.push({
8940 cls : 'input-group-addon',
8947 if (align ==='left' && this.fieldLabel.length) {
8948 // Roo.log("left and has label");
8954 cls : 'control-label col-sm-' + this.labelWidth,
8955 html : this.fieldLabel
8959 cls : "col-sm-" + (12 - this.labelWidth),
8966 } else if ( this.fieldLabel.length) {
8967 // Roo.log(" label");
8972 //cls : 'input-group-addon',
8973 html : this.fieldLabel
8983 // Roo.log(" no label && no align");
8993 if (this.disabled) {
8994 input.disabled=true;
9001 * return the real textarea element.
9003 inputEl: function ()
9005 return this.el.select('textarea.form-control',true).first();
9009 * Clear any invalid styles/messages for this field
9011 clearInvalid : function()
9014 if(!this.el || this.preventMark){ // not rendered
9018 var label = this.el.select('label', true).first();
9019 var icon = this.el.select('i.fa-star', true).first();
9025 this.el.removeClass(this.invalidClass);
9027 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9029 var feedback = this.el.select('.form-control-feedback', true).first();
9032 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9037 this.fireEvent('valid', this);
9041 * Mark this field as valid
9043 markValid : function()
9045 if(!this.el || this.preventMark){ // not rendered
9049 this.el.removeClass([this.invalidClass, this.validClass]);
9051 var feedback = this.el.select('.form-control-feedback', true).first();
9054 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9057 if(this.disabled || this.allowBlank){
9061 var label = this.el.select('label', true).first();
9062 var icon = this.el.select('i.fa-star', true).first();
9068 this.el.addClass(this.validClass);
9070 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9072 var feedback = this.el.select('.form-control-feedback', true).first();
9075 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9076 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9081 this.fireEvent('valid', this);
9085 * Mark this field as invalid
9086 * @param {String} msg The validation message
9088 markInvalid : function(msg)
9090 if(!this.el || this.preventMark){ // not rendered
9094 this.el.removeClass([this.invalidClass, this.validClass]);
9096 var feedback = this.el.select('.form-control-feedback', true).first();
9099 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9102 if(this.disabled || this.allowBlank){
9106 var label = this.el.select('label', true).first();
9107 var icon = this.el.select('i.fa-star', true).first();
9109 if(!this.getValue().length && label && !icon){
9110 this.el.createChild({
9112 cls : 'text-danger fa fa-lg fa-star',
9113 tooltip : 'This field is required',
9114 style : 'margin-right:5px;'
9118 this.el.addClass(this.invalidClass);
9120 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9122 var feedback = this.el.select('.form-control-feedback', true).first();
9125 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9127 if(this.getValue().length || this.forceFeedback){
9128 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9135 this.fireEvent('invalid', this, msg);
9143 * trigger field - base class for combo..
9148 * @class Roo.bootstrap.TriggerField
9149 * @extends Roo.bootstrap.Input
9150 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9151 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9152 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9153 * for which you can provide a custom implementation. For example:
9155 var trigger = new Roo.bootstrap.TriggerField();
9156 trigger.onTriggerClick = myTriggerFn;
9157 trigger.applyTo('my-field');
9160 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9161 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9162 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9163 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9164 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9167 * Create a new TriggerField.
9168 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9169 * to the base TextField)
9171 Roo.bootstrap.TriggerField = function(config){
9172 this.mimicing = false;
9173 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9176 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9178 * @cfg {String} triggerClass A CSS class to apply to the trigger
9181 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9186 * @cfg {Boolean} removable (true|false) special filter default false
9190 /** @cfg {Boolean} grow @hide */
9191 /** @cfg {Number} growMin @hide */
9192 /** @cfg {Number} growMax @hide */
9198 autoSize: Roo.emptyFn,
9205 actionMode : 'wrap',
9210 getAutoCreate : function(){
9212 var align = this.labelAlign || this.parentLabelAlign();
9217 cls: 'form-group' //input-group
9224 type : this.inputType,
9225 cls : 'form-control',
9226 autocomplete: 'new-password',
9227 placeholder : this.placeholder || ''
9231 input.name = this.name;
9234 input.cls += ' input-' + this.size;
9237 if (this.disabled) {
9238 input.disabled=true;
9241 var inputblock = input;
9243 if(this.hasFeedback && !this.allowBlank){
9247 cls: 'glyphicon form-control-feedback'
9250 if(this.removable && !this.editable && !this.tickable){
9252 cls : 'has-feedback',
9258 cls : 'roo-combo-removable-btn close'
9265 cls : 'has-feedback',
9274 if(this.removable && !this.editable && !this.tickable){
9276 cls : 'roo-removable',
9282 cls : 'roo-combo-removable-btn close'
9289 if (this.before || this.after) {
9292 cls : 'input-group',
9296 inputblock.cn.push({
9298 cls : 'input-group-addon',
9303 inputblock.cn.push(input);
9305 if(this.hasFeedback && !this.allowBlank){
9306 inputblock.cls += ' has-feedback';
9307 inputblock.cn.push(feedback);
9311 inputblock.cn.push({
9313 cls : 'input-group-addon',
9326 cls: 'form-hidden-field'
9340 cls: 'form-hidden-field'
9344 cls: 'roo-select2-choices',
9348 cls: 'roo-select2-search-field',
9361 cls: 'roo-select2-container input-group',
9366 // cls: 'typeahead typeahead-long dropdown-menu',
9367 // style: 'display:none'
9372 if(!this.multiple && this.showToggleBtn){
9378 if (this.caret != false) {
9381 cls: 'fa fa-' + this.caret
9388 cls : 'input-group-addon btn dropdown-toggle',
9393 cls: 'combobox-clear',
9407 combobox.cls += ' roo-select2-container-multi';
9410 if (align ==='left' && this.fieldLabel.length) {
9412 // Roo.log("left and has label");
9418 cls : 'control-label col-sm-' + this.labelWidth,
9419 html : this.fieldLabel
9423 cls : "col-sm-" + (12 - this.labelWidth),
9430 } else if ( this.fieldLabel.length) {
9431 // Roo.log(" label");
9436 //cls : 'input-group-addon',
9437 html : this.fieldLabel
9447 // Roo.log(" no label && no align");
9454 ['xs','sm','md','lg'].map(function(size){
9455 if (settings[size]) {
9456 cfg.cls += ' col-' + size + '-' + settings[size];
9467 onResize : function(w, h){
9468 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9469 // if(typeof w == 'number'){
9470 // var x = w - this.trigger.getWidth();
9471 // this.inputEl().setWidth(this.adjustWidth('input', x));
9472 // this.trigger.setStyle('left', x+'px');
9477 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9480 getResizeEl : function(){
9481 return this.inputEl();
9485 getPositionEl : function(){
9486 return this.inputEl();
9490 alignErrorIcon : function(){
9491 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9495 initEvents : function(){
9499 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9500 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9501 if(!this.multiple && this.showToggleBtn){
9502 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9503 if(this.hideTrigger){
9504 this.trigger.setDisplayed(false);
9506 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9510 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9513 if(this.removable && !this.editable && !this.tickable){
9514 var close = this.closeTriggerEl();
9517 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9518 close.on('click', this.removeBtnClick, this, close);
9522 //this.trigger.addClassOnOver('x-form-trigger-over');
9523 //this.trigger.addClassOnClick('x-form-trigger-click');
9526 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9530 closeTriggerEl : function()
9532 var close = this.el.select('.roo-combo-removable-btn', true).first();
9533 return close ? close : false;
9536 removeBtnClick : function(e, h, el)
9540 if(this.fireEvent("remove", this) !== false){
9542 this.fireEvent("afterremove", this)
9546 createList : function()
9548 this.list = Roo.get(document.body).createChild({
9550 cls: 'typeahead typeahead-long dropdown-menu',
9551 style: 'display:none'
9554 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9559 initTrigger : function(){
9564 onDestroy : function(){
9566 this.trigger.removeAllListeners();
9567 // this.trigger.remove();
9570 // this.wrap.remove();
9572 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9576 onFocus : function(){
9577 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9580 this.wrap.addClass('x-trigger-wrap-focus');
9581 this.mimicing = true;
9582 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9583 if(this.monitorTab){
9584 this.el.on("keydown", this.checkTab, this);
9591 checkTab : function(e){
9592 if(e.getKey() == e.TAB){
9598 onBlur : function(){
9603 mimicBlur : function(e, t){
9605 if(!this.wrap.contains(t) && this.validateBlur()){
9612 triggerBlur : function(){
9613 this.mimicing = false;
9614 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9615 if(this.monitorTab){
9616 this.el.un("keydown", this.checkTab, this);
9618 //this.wrap.removeClass('x-trigger-wrap-focus');
9619 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9623 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9624 validateBlur : function(e, t){
9629 onDisable : function(){
9630 this.inputEl().dom.disabled = true;
9631 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9633 // this.wrap.addClass('x-item-disabled');
9638 onEnable : function(){
9639 this.inputEl().dom.disabled = false;
9640 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9642 // this.el.removeClass('x-item-disabled');
9647 onShow : function(){
9648 var ae = this.getActionEl();
9651 ae.dom.style.display = '';
9652 ae.dom.style.visibility = 'visible';
9658 onHide : function(){
9659 var ae = this.getActionEl();
9660 ae.dom.style.display = 'none';
9664 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9665 * by an implementing function.
9667 * @param {EventObject} e
9669 onTriggerClick : Roo.emptyFn
9673 * Ext JS Library 1.1.1
9674 * Copyright(c) 2006-2007, Ext JS, LLC.
9676 * Originally Released Under LGPL - original licence link has changed is not relivant.
9679 * <script type="text/javascript">
9684 * @class Roo.data.SortTypes
9686 * Defines the default sorting (casting?) comparison functions used when sorting data.
9688 Roo.data.SortTypes = {
9690 * Default sort that does nothing
9691 * @param {Mixed} s The value being converted
9692 * @return {Mixed} The comparison value
9699 * The regular expression used to strip tags
9703 stripTagsRE : /<\/?[^>]+>/gi,
9706 * Strips all HTML tags to sort on text only
9707 * @param {Mixed} s The value being converted
9708 * @return {String} The comparison value
9710 asText : function(s){
9711 return String(s).replace(this.stripTagsRE, "");
9715 * Strips all HTML tags to sort on text only - Case insensitive
9716 * @param {Mixed} s The value being converted
9717 * @return {String} The comparison value
9719 asUCText : function(s){
9720 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9724 * Case insensitive string
9725 * @param {Mixed} s The value being converted
9726 * @return {String} The comparison value
9728 asUCString : function(s) {
9729 return String(s).toUpperCase();
9734 * @param {Mixed} s The value being converted
9735 * @return {Number} The comparison value
9737 asDate : function(s) {
9741 if(s instanceof Date){
9744 return Date.parse(String(s));
9749 * @param {Mixed} s The value being converted
9750 * @return {Float} The comparison value
9752 asFloat : function(s) {
9753 var val = parseFloat(String(s).replace(/,/g, ""));
9762 * @param {Mixed} s The value being converted
9763 * @return {Number} The comparison value
9765 asInt : function(s) {
9766 var val = parseInt(String(s).replace(/,/g, ""));
9774 * Ext JS Library 1.1.1
9775 * Copyright(c) 2006-2007, Ext JS, LLC.
9777 * Originally Released Under LGPL - original licence link has changed is not relivant.
9780 * <script type="text/javascript">
9784 * @class Roo.data.Record
9785 * Instances of this class encapsulate both record <em>definition</em> information, and record
9786 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9787 * to access Records cached in an {@link Roo.data.Store} object.<br>
9789 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9790 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9793 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9795 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9796 * {@link #create}. The parameters are the same.
9797 * @param {Array} data An associative Array of data values keyed by the field name.
9798 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9799 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9800 * not specified an integer id is generated.
9802 Roo.data.Record = function(data, id){
9803 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9808 * Generate a constructor for a specific record layout.
9809 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9810 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9811 * Each field definition object may contain the following properties: <ul>
9812 * <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,
9813 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9814 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9815 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9816 * is being used, then this is a string containing the javascript expression to reference the data relative to
9817 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9818 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9819 * this may be omitted.</p></li>
9820 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9821 * <ul><li>auto (Default, implies no conversion)</li>
9826 * <li>date</li></ul></p></li>
9827 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9828 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9829 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9830 * by the Reader into an object that will be stored in the Record. It is passed the
9831 * following parameters:<ul>
9832 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9834 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9836 * <br>usage:<br><pre><code>
9837 var TopicRecord = Roo.data.Record.create(
9838 {name: 'title', mapping: 'topic_title'},
9839 {name: 'author', mapping: 'username'},
9840 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9841 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9842 {name: 'lastPoster', mapping: 'user2'},
9843 {name: 'excerpt', mapping: 'post_text'}
9846 var myNewRecord = new TopicRecord({
9847 title: 'Do my job please',
9850 lastPost: new Date(),
9851 lastPoster: 'Animal',
9852 excerpt: 'No way dude!'
9854 myStore.add(myNewRecord);
9859 Roo.data.Record.create = function(o){
9861 f.superclass.constructor.apply(this, arguments);
9863 Roo.extend(f, Roo.data.Record);
9864 var p = f.prototype;
9865 p.fields = new Roo.util.MixedCollection(false, function(field){
9868 for(var i = 0, len = o.length; i < len; i++){
9869 p.fields.add(new Roo.data.Field(o[i]));
9871 f.getField = function(name){
9872 return p.fields.get(name);
9877 Roo.data.Record.AUTO_ID = 1000;
9878 Roo.data.Record.EDIT = 'edit';
9879 Roo.data.Record.REJECT = 'reject';
9880 Roo.data.Record.COMMIT = 'commit';
9882 Roo.data.Record.prototype = {
9884 * Readonly flag - true if this record has been modified.
9893 join : function(store){
9898 * Set the named field to the specified value.
9899 * @param {String} name The name of the field to set.
9900 * @param {Object} value The value to set the field to.
9902 set : function(name, value){
9903 if(this.data[name] == value){
9910 if(typeof this.modified[name] == 'undefined'){
9911 this.modified[name] = this.data[name];
9913 this.data[name] = value;
9914 if(!this.editing && this.store){
9915 this.store.afterEdit(this);
9920 * Get the value of the named field.
9921 * @param {String} name The name of the field to get the value of.
9922 * @return {Object} The value of the field.
9924 get : function(name){
9925 return this.data[name];
9929 beginEdit : function(){
9930 this.editing = true;
9935 cancelEdit : function(){
9936 this.editing = false;
9937 delete this.modified;
9941 endEdit : function(){
9942 this.editing = false;
9943 if(this.dirty && this.store){
9944 this.store.afterEdit(this);
9949 * Usually called by the {@link Roo.data.Store} which owns the Record.
9950 * Rejects all changes made to the Record since either creation, or the last commit operation.
9951 * Modified fields are reverted to their original values.
9953 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9954 * of reject operations.
9956 reject : function(){
9957 var m = this.modified;
9959 if(typeof m[n] != "function"){
9960 this.data[n] = m[n];
9964 delete this.modified;
9965 this.editing = false;
9967 this.store.afterReject(this);
9972 * Usually called by the {@link Roo.data.Store} which owns the Record.
9973 * Commits all changes made to the Record since either creation, or the last commit operation.
9975 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9976 * of commit operations.
9978 commit : function(){
9980 delete this.modified;
9981 this.editing = false;
9983 this.store.afterCommit(this);
9988 hasError : function(){
9989 return this.error != null;
9993 clearError : function(){
9998 * Creates a copy of this record.
9999 * @param {String} id (optional) A new record id if you don't want to use this record's id
10002 copy : function(newId) {
10003 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10007 * Ext JS Library 1.1.1
10008 * Copyright(c) 2006-2007, Ext JS, LLC.
10010 * Originally Released Under LGPL - original licence link has changed is not relivant.
10013 * <script type="text/javascript">
10019 * @class Roo.data.Store
10020 * @extends Roo.util.Observable
10021 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10022 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10024 * 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
10025 * has no knowledge of the format of the data returned by the Proxy.<br>
10027 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10028 * instances from the data object. These records are cached and made available through accessor functions.
10030 * Creates a new Store.
10031 * @param {Object} config A config object containing the objects needed for the Store to access data,
10032 * and read the data into Records.
10034 Roo.data.Store = function(config){
10035 this.data = new Roo.util.MixedCollection(false);
10036 this.data.getKey = function(o){
10039 this.baseParams = {};
10041 this.paramNames = {
10046 "multisort" : "_multisort"
10049 if(config && config.data){
10050 this.inlineData = config.data;
10051 delete config.data;
10054 Roo.apply(this, config);
10056 if(this.reader){ // reader passed
10057 this.reader = Roo.factory(this.reader, Roo.data);
10058 this.reader.xmodule = this.xmodule || false;
10059 if(!this.recordType){
10060 this.recordType = this.reader.recordType;
10062 if(this.reader.onMetaChange){
10063 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10067 if(this.recordType){
10068 this.fields = this.recordType.prototype.fields;
10070 this.modified = [];
10074 * @event datachanged
10075 * Fires when the data cache has changed, and a widget which is using this Store
10076 * as a Record cache should refresh its view.
10077 * @param {Store} this
10079 datachanged : true,
10081 * @event metachange
10082 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10083 * @param {Store} this
10084 * @param {Object} meta The JSON metadata
10089 * Fires when Records have been added to the Store
10090 * @param {Store} this
10091 * @param {Roo.data.Record[]} records The array of Records added
10092 * @param {Number} index The index at which the record(s) were added
10097 * Fires when a Record has been removed from the Store
10098 * @param {Store} this
10099 * @param {Roo.data.Record} record The Record that was removed
10100 * @param {Number} index The index at which the record was removed
10105 * Fires when a Record has been updated
10106 * @param {Store} this
10107 * @param {Roo.data.Record} record The Record that was updated
10108 * @param {String} operation The update operation being performed. Value may be one of:
10110 Roo.data.Record.EDIT
10111 Roo.data.Record.REJECT
10112 Roo.data.Record.COMMIT
10118 * Fires when the data cache has been cleared.
10119 * @param {Store} this
10123 * @event beforeload
10124 * Fires before a request is made for a new data object. If the beforeload handler returns false
10125 * the load action will be canceled.
10126 * @param {Store} this
10127 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10131 * @event beforeloadadd
10132 * Fires after a new set of Records has been loaded.
10133 * @param {Store} this
10134 * @param {Roo.data.Record[]} records The Records that were loaded
10135 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10137 beforeloadadd : true,
10140 * Fires after a new set of Records has been loaded, before they are added to the store.
10141 * @param {Store} this
10142 * @param {Roo.data.Record[]} records The Records that were loaded
10143 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10144 * @params {Object} return from reader
10148 * @event loadexception
10149 * Fires if an exception occurs in the Proxy during loading.
10150 * Called with the signature of the Proxy's "loadexception" event.
10151 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10154 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10155 * @param {Object} load options
10156 * @param {Object} jsonData from your request (normally this contains the Exception)
10158 loadexception : true
10162 this.proxy = Roo.factory(this.proxy, Roo.data);
10163 this.proxy.xmodule = this.xmodule || false;
10164 this.relayEvents(this.proxy, ["loadexception"]);
10166 this.sortToggle = {};
10167 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10169 Roo.data.Store.superclass.constructor.call(this);
10171 if(this.inlineData){
10172 this.loadData(this.inlineData);
10173 delete this.inlineData;
10177 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10179 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10180 * without a remote query - used by combo/forms at present.
10184 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10187 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10190 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10191 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10194 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10195 * on any HTTP request
10198 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10201 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10205 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10206 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10208 remoteSort : false,
10211 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10212 * loaded or when a record is removed. (defaults to false).
10214 pruneModifiedRecords : false,
10217 lastOptions : null,
10220 * Add Records to the Store and fires the add event.
10221 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10223 add : function(records){
10224 records = [].concat(records);
10225 for(var i = 0, len = records.length; i < len; i++){
10226 records[i].join(this);
10228 var index = this.data.length;
10229 this.data.addAll(records);
10230 this.fireEvent("add", this, records, index);
10234 * Remove a Record from the Store and fires the remove event.
10235 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10237 remove : function(record){
10238 var index = this.data.indexOf(record);
10239 this.data.removeAt(index);
10240 if(this.pruneModifiedRecords){
10241 this.modified.remove(record);
10243 this.fireEvent("remove", this, record, index);
10247 * Remove all Records from the Store and fires the clear event.
10249 removeAll : function(){
10251 if(this.pruneModifiedRecords){
10252 this.modified = [];
10254 this.fireEvent("clear", this);
10258 * Inserts Records to the Store at the given index and fires the add event.
10259 * @param {Number} index The start index at which to insert the passed Records.
10260 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10262 insert : function(index, records){
10263 records = [].concat(records);
10264 for(var i = 0, len = records.length; i < len; i++){
10265 this.data.insert(index, records[i]);
10266 records[i].join(this);
10268 this.fireEvent("add", this, records, index);
10272 * Get the index within the cache of the passed Record.
10273 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10274 * @return {Number} The index of the passed Record. Returns -1 if not found.
10276 indexOf : function(record){
10277 return this.data.indexOf(record);
10281 * Get the index within the cache of the Record with the passed id.
10282 * @param {String} id The id of the Record to find.
10283 * @return {Number} The index of the Record. Returns -1 if not found.
10285 indexOfId : function(id){
10286 return this.data.indexOfKey(id);
10290 * Get the Record with the specified id.
10291 * @param {String} id The id of the Record to find.
10292 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10294 getById : function(id){
10295 return this.data.key(id);
10299 * Get the Record at the specified index.
10300 * @param {Number} index The index of the Record to find.
10301 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10303 getAt : function(index){
10304 return this.data.itemAt(index);
10308 * Returns a range of Records between specified indices.
10309 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10310 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10311 * @return {Roo.data.Record[]} An array of Records
10313 getRange : function(start, end){
10314 return this.data.getRange(start, end);
10318 storeOptions : function(o){
10319 o = Roo.apply({}, o);
10322 this.lastOptions = o;
10326 * Loads the Record cache from the configured Proxy using the configured Reader.
10328 * If using remote paging, then the first load call must specify the <em>start</em>
10329 * and <em>limit</em> properties in the options.params property to establish the initial
10330 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10332 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10333 * and this call will return before the new data has been loaded. Perform any post-processing
10334 * in a callback function, or in a "load" event handler.</strong>
10336 * @param {Object} options An object containing properties which control loading options:<ul>
10337 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10338 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10339 * passed the following arguments:<ul>
10340 * <li>r : Roo.data.Record[]</li>
10341 * <li>options: Options object from the load call</li>
10342 * <li>success: Boolean success indicator</li></ul></li>
10343 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10344 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10347 load : function(options){
10348 options = options || {};
10349 if(this.fireEvent("beforeload", this, options) !== false){
10350 this.storeOptions(options);
10351 var p = Roo.apply(options.params || {}, this.baseParams);
10352 // if meta was not loaded from remote source.. try requesting it.
10353 if (!this.reader.metaFromRemote) {
10354 p._requestMeta = 1;
10356 if(this.sortInfo && this.remoteSort){
10357 var pn = this.paramNames;
10358 p[pn["sort"]] = this.sortInfo.field;
10359 p[pn["dir"]] = this.sortInfo.direction;
10361 if (this.multiSort) {
10362 var pn = this.paramNames;
10363 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10366 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10371 * Reloads the Record cache from the configured Proxy using the configured Reader and
10372 * the options from the last load operation performed.
10373 * @param {Object} options (optional) An object containing properties which may override the options
10374 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10375 * the most recently used options are reused).
10377 reload : function(options){
10378 this.load(Roo.applyIf(options||{}, this.lastOptions));
10382 // Called as a callback by the Reader during a load operation.
10383 loadRecords : function(o, options, success){
10384 if(!o || success === false){
10385 if(success !== false){
10386 this.fireEvent("load", this, [], options, o);
10388 if(options.callback){
10389 options.callback.call(options.scope || this, [], options, false);
10393 // if data returned failure - throw an exception.
10394 if (o.success === false) {
10395 // show a message if no listener is registered.
10396 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10397 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10399 // loadmask wil be hooked into this..
10400 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10403 var r = o.records, t = o.totalRecords || r.length;
10405 this.fireEvent("beforeloadadd", this, r, options, o);
10407 if(!options || options.add !== true){
10408 if(this.pruneModifiedRecords){
10409 this.modified = [];
10411 for(var i = 0, len = r.length; i < len; i++){
10415 this.data = this.snapshot;
10416 delete this.snapshot;
10419 this.data.addAll(r);
10420 this.totalLength = t;
10422 this.fireEvent("datachanged", this);
10424 this.totalLength = Math.max(t, this.data.length+r.length);
10427 this.fireEvent("load", this, r, options, o);
10428 if(options.callback){
10429 options.callback.call(options.scope || this, r, options, true);
10435 * Loads data from a passed data block. A Reader which understands the format of the data
10436 * must have been configured in the constructor.
10437 * @param {Object} data The data block from which to read the Records. The format of the data expected
10438 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10439 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10441 loadData : function(o, append){
10442 var r = this.reader.readRecords(o);
10443 this.loadRecords(r, {add: append}, true);
10447 * Gets the number of cached records.
10449 * <em>If using paging, this may not be the total size of the dataset. If the data object
10450 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10451 * the data set size</em>
10453 getCount : function(){
10454 return this.data.length || 0;
10458 * Gets the total number of records in the dataset as returned by the server.
10460 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10461 * the dataset size</em>
10463 getTotalCount : function(){
10464 return this.totalLength || 0;
10468 * Returns the sort state of the Store as an object with two properties:
10470 field {String} The name of the field by which the Records are sorted
10471 direction {String} The sort order, "ASC" or "DESC"
10474 getSortState : function(){
10475 return this.sortInfo;
10479 applySort : function(){
10480 if(this.sortInfo && !this.remoteSort){
10481 var s = this.sortInfo, f = s.field;
10482 var st = this.fields.get(f).sortType;
10483 var fn = function(r1, r2){
10484 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10485 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10487 this.data.sort(s.direction, fn);
10488 if(this.snapshot && this.snapshot != this.data){
10489 this.snapshot.sort(s.direction, fn);
10495 * Sets the default sort column and order to be used by the next load operation.
10496 * @param {String} fieldName The name of the field to sort by.
10497 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10499 setDefaultSort : function(field, dir){
10500 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10504 * Sort the Records.
10505 * If remote sorting is used, the sort is performed on the server, and the cache is
10506 * reloaded. If local sorting is used, the cache is sorted internally.
10507 * @param {String} fieldName The name of the field to sort by.
10508 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10510 sort : function(fieldName, dir){
10511 var f = this.fields.get(fieldName);
10513 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10515 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10516 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10521 this.sortToggle[f.name] = dir;
10522 this.sortInfo = {field: f.name, direction: dir};
10523 if(!this.remoteSort){
10525 this.fireEvent("datachanged", this);
10527 this.load(this.lastOptions);
10532 * Calls the specified function for each of the Records in the cache.
10533 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10534 * Returning <em>false</em> aborts and exits the iteration.
10535 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10537 each : function(fn, scope){
10538 this.data.each(fn, scope);
10542 * Gets all records modified since the last commit. Modified records are persisted across load operations
10543 * (e.g., during paging).
10544 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10546 getModifiedRecords : function(){
10547 return this.modified;
10551 createFilterFn : function(property, value, anyMatch){
10552 if(!value.exec){ // not a regex
10553 value = String(value);
10554 if(value.length == 0){
10557 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10559 return function(r){
10560 return value.test(r.data[property]);
10565 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10566 * @param {String} property A field on your records
10567 * @param {Number} start The record index to start at (defaults to 0)
10568 * @param {Number} end The last record index to include (defaults to length - 1)
10569 * @return {Number} The sum
10571 sum : function(property, start, end){
10572 var rs = this.data.items, v = 0;
10573 start = start || 0;
10574 end = (end || end === 0) ? end : rs.length-1;
10576 for(var i = start; i <= end; i++){
10577 v += (rs[i].data[property] || 0);
10583 * Filter the records by a specified property.
10584 * @param {String} field A field on your records
10585 * @param {String/RegExp} value Either a string that the field
10586 * should start with or a RegExp to test against the field
10587 * @param {Boolean} anyMatch True to match any part not just the beginning
10589 filter : function(property, value, anyMatch){
10590 var fn = this.createFilterFn(property, value, anyMatch);
10591 return fn ? this.filterBy(fn) : this.clearFilter();
10595 * Filter by a function. The specified function will be called with each
10596 * record in this data source. If the function returns true the record is included,
10597 * otherwise it is filtered.
10598 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10599 * @param {Object} scope (optional) The scope of the function (defaults to this)
10601 filterBy : function(fn, scope){
10602 this.snapshot = this.snapshot || this.data;
10603 this.data = this.queryBy(fn, scope||this);
10604 this.fireEvent("datachanged", this);
10608 * Query the records by a specified property.
10609 * @param {String} field A field on your records
10610 * @param {String/RegExp} value Either a string that the field
10611 * should start with or a RegExp to test against the field
10612 * @param {Boolean} anyMatch True to match any part not just the beginning
10613 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10615 query : function(property, value, anyMatch){
10616 var fn = this.createFilterFn(property, value, anyMatch);
10617 return fn ? this.queryBy(fn) : this.data.clone();
10621 * Query by a function. The specified function will be called with each
10622 * record in this data source. If the function returns true the record is included
10624 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10625 * @param {Object} scope (optional) The scope of the function (defaults to this)
10626 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10628 queryBy : function(fn, scope){
10629 var data = this.snapshot || this.data;
10630 return data.filterBy(fn, scope||this);
10634 * Collects unique values for a particular dataIndex from this store.
10635 * @param {String} dataIndex The property to collect
10636 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10637 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10638 * @return {Array} An array of the unique values
10640 collect : function(dataIndex, allowNull, bypassFilter){
10641 var d = (bypassFilter === true && this.snapshot) ?
10642 this.snapshot.items : this.data.items;
10643 var v, sv, r = [], l = {};
10644 for(var i = 0, len = d.length; i < len; i++){
10645 v = d[i].data[dataIndex];
10647 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10656 * Revert to a view of the Record cache with no filtering applied.
10657 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10659 clearFilter : function(suppressEvent){
10660 if(this.snapshot && this.snapshot != this.data){
10661 this.data = this.snapshot;
10662 delete this.snapshot;
10663 if(suppressEvent !== true){
10664 this.fireEvent("datachanged", this);
10670 afterEdit : function(record){
10671 if(this.modified.indexOf(record) == -1){
10672 this.modified.push(record);
10674 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10678 afterReject : function(record){
10679 this.modified.remove(record);
10680 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10684 afterCommit : function(record){
10685 this.modified.remove(record);
10686 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10690 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10691 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10693 commitChanges : function(){
10694 var m = this.modified.slice(0);
10695 this.modified = [];
10696 for(var i = 0, len = m.length; i < len; i++){
10702 * Cancel outstanding changes on all changed records.
10704 rejectChanges : function(){
10705 var m = this.modified.slice(0);
10706 this.modified = [];
10707 for(var i = 0, len = m.length; i < len; i++){
10712 onMetaChange : function(meta, rtype, o){
10713 this.recordType = rtype;
10714 this.fields = rtype.prototype.fields;
10715 delete this.snapshot;
10716 this.sortInfo = meta.sortInfo || this.sortInfo;
10717 this.modified = [];
10718 this.fireEvent('metachange', this, this.reader.meta);
10721 moveIndex : function(data, type)
10723 var index = this.indexOf(data);
10725 var newIndex = index + type;
10729 this.insert(newIndex, data);
10734 * Ext JS Library 1.1.1
10735 * Copyright(c) 2006-2007, Ext JS, LLC.
10737 * Originally Released Under LGPL - original licence link has changed is not relivant.
10740 * <script type="text/javascript">
10744 * @class Roo.data.SimpleStore
10745 * @extends Roo.data.Store
10746 * Small helper class to make creating Stores from Array data easier.
10747 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10748 * @cfg {Array} fields An array of field definition objects, or field name strings.
10749 * @cfg {Array} data The multi-dimensional array of data
10751 * @param {Object} config
10753 Roo.data.SimpleStore = function(config){
10754 Roo.data.SimpleStore.superclass.constructor.call(this, {
10756 reader: new Roo.data.ArrayReader({
10759 Roo.data.Record.create(config.fields)
10761 proxy : new Roo.data.MemoryProxy(config.data)
10765 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10767 * Ext JS Library 1.1.1
10768 * Copyright(c) 2006-2007, Ext JS, LLC.
10770 * Originally Released Under LGPL - original licence link has changed is not relivant.
10773 * <script type="text/javascript">
10778 * @extends Roo.data.Store
10779 * @class Roo.data.JsonStore
10780 * Small helper class to make creating Stores for JSON data easier. <br/>
10782 var store = new Roo.data.JsonStore({
10783 url: 'get-images.php',
10785 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10788 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10789 * JsonReader and HttpProxy (unless inline data is provided).</b>
10790 * @cfg {Array} fields An array of field definition objects, or field name strings.
10792 * @param {Object} config
10794 Roo.data.JsonStore = function(c){
10795 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10796 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10797 reader: new Roo.data.JsonReader(c, c.fields)
10800 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10802 * Ext JS Library 1.1.1
10803 * Copyright(c) 2006-2007, Ext JS, LLC.
10805 * Originally Released Under LGPL - original licence link has changed is not relivant.
10808 * <script type="text/javascript">
10812 Roo.data.Field = function(config){
10813 if(typeof config == "string"){
10814 config = {name: config};
10816 Roo.apply(this, config);
10819 this.type = "auto";
10822 var st = Roo.data.SortTypes;
10823 // named sortTypes are supported, here we look them up
10824 if(typeof this.sortType == "string"){
10825 this.sortType = st[this.sortType];
10828 // set default sortType for strings and dates
10829 if(!this.sortType){
10832 this.sortType = st.asUCString;
10835 this.sortType = st.asDate;
10838 this.sortType = st.none;
10843 var stripRe = /[\$,%]/g;
10845 // prebuilt conversion function for this field, instead of
10846 // switching every time we're reading a value
10848 var cv, dateFormat = this.dateFormat;
10853 cv = function(v){ return v; };
10856 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10860 return v !== undefined && v !== null && v !== '' ?
10861 parseInt(String(v).replace(stripRe, ""), 10) : '';
10866 return v !== undefined && v !== null && v !== '' ?
10867 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10872 cv = function(v){ return v === true || v === "true" || v == 1; };
10879 if(v instanceof Date){
10883 if(dateFormat == "timestamp"){
10884 return new Date(v*1000);
10886 return Date.parseDate(v, dateFormat);
10888 var parsed = Date.parse(v);
10889 return parsed ? new Date(parsed) : null;
10898 Roo.data.Field.prototype = {
10906 * Ext JS Library 1.1.1
10907 * Copyright(c) 2006-2007, Ext JS, LLC.
10909 * Originally Released Under LGPL - original licence link has changed is not relivant.
10912 * <script type="text/javascript">
10915 // Base class for reading structured data from a data source. This class is intended to be
10916 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10919 * @class Roo.data.DataReader
10920 * Base class for reading structured data from a data source. This class is intended to be
10921 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10924 Roo.data.DataReader = function(meta, recordType){
10928 this.recordType = recordType instanceof Array ?
10929 Roo.data.Record.create(recordType) : recordType;
10932 Roo.data.DataReader.prototype = {
10934 * Create an empty record
10935 * @param {Object} data (optional) - overlay some values
10936 * @return {Roo.data.Record} record created.
10938 newRow : function(d) {
10940 this.recordType.prototype.fields.each(function(c) {
10942 case 'int' : da[c.name] = 0; break;
10943 case 'date' : da[c.name] = new Date(); break;
10944 case 'float' : da[c.name] = 0.0; break;
10945 case 'boolean' : da[c.name] = false; break;
10946 default : da[c.name] = ""; break;
10950 return new this.recordType(Roo.apply(da, d));
10955 * Ext JS Library 1.1.1
10956 * Copyright(c) 2006-2007, Ext JS, LLC.
10958 * Originally Released Under LGPL - original licence link has changed is not relivant.
10961 * <script type="text/javascript">
10965 * @class Roo.data.DataProxy
10966 * @extends Roo.data.Observable
10967 * This class is an abstract base class for implementations which provide retrieval of
10968 * unformatted data objects.<br>
10970 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10971 * (of the appropriate type which knows how to parse the data object) to provide a block of
10972 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10974 * Custom implementations must implement the load method as described in
10975 * {@link Roo.data.HttpProxy#load}.
10977 Roo.data.DataProxy = function(){
10980 * @event beforeload
10981 * Fires before a network request is made to retrieve a data object.
10982 * @param {Object} This DataProxy object.
10983 * @param {Object} params The params parameter to the load function.
10988 * Fires before the load method's callback is called.
10989 * @param {Object} This DataProxy object.
10990 * @param {Object} o The data object.
10991 * @param {Object} arg The callback argument object passed to the load function.
10995 * @event loadexception
10996 * Fires if an Exception occurs during data retrieval.
10997 * @param {Object} This DataProxy object.
10998 * @param {Object} o The data object.
10999 * @param {Object} arg The callback argument object passed to the load function.
11000 * @param {Object} e The Exception.
11002 loadexception : true
11004 Roo.data.DataProxy.superclass.constructor.call(this);
11007 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11010 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11014 * Ext JS Library 1.1.1
11015 * Copyright(c) 2006-2007, Ext JS, LLC.
11017 * Originally Released Under LGPL - original licence link has changed is not relivant.
11020 * <script type="text/javascript">
11023 * @class Roo.data.MemoryProxy
11024 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11025 * to the Reader when its load method is called.
11027 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11029 Roo.data.MemoryProxy = function(data){
11033 Roo.data.MemoryProxy.superclass.constructor.call(this);
11037 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11040 * Load data from the requested source (in this case an in-memory
11041 * data object passed to the constructor), read the data object into
11042 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11043 * process that block using the passed callback.
11044 * @param {Object} params This parameter is not used by the MemoryProxy class.
11045 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11046 * object into a block of Roo.data.Records.
11047 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11048 * The function must be passed <ul>
11049 * <li>The Record block object</li>
11050 * <li>The "arg" argument from the load function</li>
11051 * <li>A boolean success indicator</li>
11053 * @param {Object} scope The scope in which to call the callback
11054 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11056 load : function(params, reader, callback, scope, arg){
11057 params = params || {};
11060 result = reader.readRecords(this.data);
11062 this.fireEvent("loadexception", this, arg, null, e);
11063 callback.call(scope, null, arg, false);
11066 callback.call(scope, result, arg, true);
11070 update : function(params, records){
11075 * Ext JS Library 1.1.1
11076 * Copyright(c) 2006-2007, Ext JS, LLC.
11078 * Originally Released Under LGPL - original licence link has changed is not relivant.
11081 * <script type="text/javascript">
11084 * @class Roo.data.HttpProxy
11085 * @extends Roo.data.DataProxy
11086 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11087 * configured to reference a certain URL.<br><br>
11089 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11090 * from which the running page was served.<br><br>
11092 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11094 * Be aware that to enable the browser to parse an XML document, the server must set
11095 * the Content-Type header in the HTTP response to "text/xml".
11097 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11098 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11099 * will be used to make the request.
11101 Roo.data.HttpProxy = function(conn){
11102 Roo.data.HttpProxy.superclass.constructor.call(this);
11103 // is conn a conn config or a real conn?
11105 this.useAjax = !conn || !conn.events;
11109 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11110 // thse are take from connection...
11113 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11116 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11117 * extra parameters to each request made by this object. (defaults to undefined)
11120 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11121 * to each request made by this object. (defaults to undefined)
11124 * @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)
11127 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11130 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11136 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11140 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11141 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11142 * a finer-grained basis than the DataProxy events.
11144 getConnection : function(){
11145 return this.useAjax ? Roo.Ajax : this.conn;
11149 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11150 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11151 * process that block using the passed callback.
11152 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11153 * for the request to the remote server.
11154 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11155 * object into a block of Roo.data.Records.
11156 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11157 * The function must be passed <ul>
11158 * <li>The Record block object</li>
11159 * <li>The "arg" argument from the load function</li>
11160 * <li>A boolean success indicator</li>
11162 * @param {Object} scope The scope in which to call the callback
11163 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11165 load : function(params, reader, callback, scope, arg){
11166 if(this.fireEvent("beforeload", this, params) !== false){
11168 params : params || {},
11170 callback : callback,
11175 callback : this.loadResponse,
11179 Roo.applyIf(o, this.conn);
11180 if(this.activeRequest){
11181 Roo.Ajax.abort(this.activeRequest);
11183 this.activeRequest = Roo.Ajax.request(o);
11185 this.conn.request(o);
11188 callback.call(scope||this, null, arg, false);
11193 loadResponse : function(o, success, response){
11194 delete this.activeRequest;
11196 this.fireEvent("loadexception", this, o, response);
11197 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11202 result = o.reader.read(response);
11204 this.fireEvent("loadexception", this, o, response, e);
11205 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11209 this.fireEvent("load", this, o, o.request.arg);
11210 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11214 update : function(dataSet){
11219 updateResponse : function(dataSet){
11224 * Ext JS Library 1.1.1
11225 * Copyright(c) 2006-2007, Ext JS, LLC.
11227 * Originally Released Under LGPL - original licence link has changed is not relivant.
11230 * <script type="text/javascript">
11234 * @class Roo.data.ScriptTagProxy
11235 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11236 * other than the originating domain of the running page.<br><br>
11238 * <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
11239 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11241 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11242 * source code that is used as the source inside a <script> tag.<br><br>
11244 * In order for the browser to process the returned data, the server must wrap the data object
11245 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11246 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11247 * depending on whether the callback name was passed:
11250 boolean scriptTag = false;
11251 String cb = request.getParameter("callback");
11254 response.setContentType("text/javascript");
11256 response.setContentType("application/x-json");
11258 Writer out = response.getWriter();
11260 out.write(cb + "(");
11262 out.print(dataBlock.toJsonString());
11269 * @param {Object} config A configuration object.
11271 Roo.data.ScriptTagProxy = function(config){
11272 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11273 Roo.apply(this, config);
11274 this.head = document.getElementsByTagName("head")[0];
11277 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11279 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11281 * @cfg {String} url The URL from which to request the data object.
11284 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11288 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11289 * the server the name of the callback function set up by the load call to process the returned data object.
11290 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11291 * javascript output which calls this named function passing the data object as its only parameter.
11293 callbackParam : "callback",
11295 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11296 * name to the request.
11301 * Load data from the configured URL, read the data object into
11302 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11303 * process that block using the passed callback.
11304 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11305 * for the request to the remote server.
11306 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11307 * object into a block of Roo.data.Records.
11308 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11309 * The function must be passed <ul>
11310 * <li>The Record block object</li>
11311 * <li>The "arg" argument from the load function</li>
11312 * <li>A boolean success indicator</li>
11314 * @param {Object} scope The scope in which to call the callback
11315 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11317 load : function(params, reader, callback, scope, arg){
11318 if(this.fireEvent("beforeload", this, params) !== false){
11320 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11322 var url = this.url;
11323 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11325 url += "&_dc=" + (new Date().getTime());
11327 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11330 cb : "stcCallback"+transId,
11331 scriptId : "stcScript"+transId,
11335 callback : callback,
11341 window[trans.cb] = function(o){
11342 conn.handleResponse(o, trans);
11345 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11347 if(this.autoAbort !== false){
11351 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11353 var script = document.createElement("script");
11354 script.setAttribute("src", url);
11355 script.setAttribute("type", "text/javascript");
11356 script.setAttribute("id", trans.scriptId);
11357 this.head.appendChild(script);
11359 this.trans = trans;
11361 callback.call(scope||this, null, arg, false);
11366 isLoading : function(){
11367 return this.trans ? true : false;
11371 * Abort the current server request.
11373 abort : function(){
11374 if(this.isLoading()){
11375 this.destroyTrans(this.trans);
11380 destroyTrans : function(trans, isLoaded){
11381 this.head.removeChild(document.getElementById(trans.scriptId));
11382 clearTimeout(trans.timeoutId);
11384 window[trans.cb] = undefined;
11386 delete window[trans.cb];
11389 // if hasn't been loaded, wait for load to remove it to prevent script error
11390 window[trans.cb] = function(){
11391 window[trans.cb] = undefined;
11393 delete window[trans.cb];
11400 handleResponse : function(o, trans){
11401 this.trans = false;
11402 this.destroyTrans(trans, true);
11405 result = trans.reader.readRecords(o);
11407 this.fireEvent("loadexception", this, o, trans.arg, e);
11408 trans.callback.call(trans.scope||window, null, trans.arg, false);
11411 this.fireEvent("load", this, o, trans.arg);
11412 trans.callback.call(trans.scope||window, result, trans.arg, true);
11416 handleFailure : function(trans){
11417 this.trans = false;
11418 this.destroyTrans(trans, false);
11419 this.fireEvent("loadexception", this, null, trans.arg);
11420 trans.callback.call(trans.scope||window, null, trans.arg, false);
11424 * Ext JS Library 1.1.1
11425 * Copyright(c) 2006-2007, Ext JS, LLC.
11427 * Originally Released Under LGPL - original licence link has changed is not relivant.
11430 * <script type="text/javascript">
11434 * @class Roo.data.JsonReader
11435 * @extends Roo.data.DataReader
11436 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11437 * based on mappings in a provided Roo.data.Record constructor.
11439 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11440 * in the reply previously.
11445 var RecordDef = Roo.data.Record.create([
11446 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11447 {name: 'occupation'} // This field will use "occupation" as the mapping.
11449 var myReader = new Roo.data.JsonReader({
11450 totalProperty: "results", // The property which contains the total dataset size (optional)
11451 root: "rows", // The property which contains an Array of row objects
11452 id: "id" // The property within each row object that provides an ID for the record (optional)
11456 * This would consume a JSON file like this:
11458 { 'results': 2, 'rows': [
11459 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11460 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11463 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11464 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11465 * paged from the remote server.
11466 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11467 * @cfg {String} root name of the property which contains the Array of row objects.
11468 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11469 * @cfg {Array} fields Array of field definition objects
11471 * Create a new JsonReader
11472 * @param {Object} meta Metadata configuration options
11473 * @param {Object} recordType Either an Array of field definition objects,
11474 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11476 Roo.data.JsonReader = function(meta, recordType){
11479 // set some defaults:
11480 Roo.applyIf(meta, {
11481 totalProperty: 'total',
11482 successProperty : 'success',
11487 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11489 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11492 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11493 * Used by Store query builder to append _requestMeta to params.
11496 metaFromRemote : false,
11498 * This method is only used by a DataProxy which has retrieved data from a remote server.
11499 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11500 * @return {Object} data A data block which is used by an Roo.data.Store object as
11501 * a cache of Roo.data.Records.
11503 read : function(response){
11504 var json = response.responseText;
11506 var o = /* eval:var:o */ eval("("+json+")");
11508 throw {message: "JsonReader.read: Json object not found"};
11514 this.metaFromRemote = true;
11515 this.meta = o.metaData;
11516 this.recordType = Roo.data.Record.create(o.metaData.fields);
11517 this.onMetaChange(this.meta, this.recordType, o);
11519 return this.readRecords(o);
11522 // private function a store will implement
11523 onMetaChange : function(meta, recordType, o){
11530 simpleAccess: function(obj, subsc) {
11537 getJsonAccessor: function(){
11539 return function(expr) {
11541 return(re.test(expr))
11542 ? new Function("obj", "return obj." + expr)
11547 return Roo.emptyFn;
11552 * Create a data block containing Roo.data.Records from an XML document.
11553 * @param {Object} o An object which contains an Array of row objects in the property specified
11554 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11555 * which contains the total size of the dataset.
11556 * @return {Object} data A data block which is used by an Roo.data.Store object as
11557 * a cache of Roo.data.Records.
11559 readRecords : function(o){
11561 * After any data loads, the raw JSON data is available for further custom processing.
11565 var s = this.meta, Record = this.recordType,
11566 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11568 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11570 if(s.totalProperty) {
11571 this.getTotal = this.getJsonAccessor(s.totalProperty);
11573 if(s.successProperty) {
11574 this.getSuccess = this.getJsonAccessor(s.successProperty);
11576 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11578 var g = this.getJsonAccessor(s.id);
11579 this.getId = function(rec) {
11581 return (r === undefined || r === "") ? null : r;
11584 this.getId = function(){return null;};
11587 for(var jj = 0; jj < fl; jj++){
11589 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11590 this.ef[jj] = this.getJsonAccessor(map);
11594 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11595 if(s.totalProperty){
11596 var vt = parseInt(this.getTotal(o), 10);
11601 if(s.successProperty){
11602 var vs = this.getSuccess(o);
11603 if(vs === false || vs === 'false'){
11608 for(var i = 0; i < c; i++){
11611 var id = this.getId(n);
11612 for(var j = 0; j < fl; j++){
11614 var v = this.ef[j](n);
11616 Roo.log('missing convert for ' + f.name);
11620 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11622 var record = new Record(values, id);
11624 records[i] = record;
11630 totalRecords : totalRecords
11635 * Ext JS Library 1.1.1
11636 * Copyright(c) 2006-2007, Ext JS, LLC.
11638 * Originally Released Under LGPL - original licence link has changed is not relivant.
11641 * <script type="text/javascript">
11645 * @class Roo.data.ArrayReader
11646 * @extends Roo.data.DataReader
11647 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11648 * Each element of that Array represents a row of data fields. The
11649 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11650 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11654 var RecordDef = Roo.data.Record.create([
11655 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11656 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11658 var myReader = new Roo.data.ArrayReader({
11659 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11663 * This would consume an Array like this:
11665 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11667 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11669 * Create a new JsonReader
11670 * @param {Object} meta Metadata configuration options.
11671 * @param {Object} recordType Either an Array of field definition objects
11672 * as specified to {@link Roo.data.Record#create},
11673 * or an {@link Roo.data.Record} object
11674 * created using {@link Roo.data.Record#create}.
11676 Roo.data.ArrayReader = function(meta, recordType){
11677 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11680 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11682 * Create a data block containing Roo.data.Records from an XML document.
11683 * @param {Object} o An Array of row objects which represents the dataset.
11684 * @return {Object} data A data block which is used by an Roo.data.Store object as
11685 * a cache of Roo.data.Records.
11687 readRecords : function(o){
11688 var sid = this.meta ? this.meta.id : null;
11689 var recordType = this.recordType, fields = recordType.prototype.fields;
11692 for(var i = 0; i < root.length; i++){
11695 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11696 for(var j = 0, jlen = fields.length; j < jlen; j++){
11697 var f = fields.items[j];
11698 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11699 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11701 values[f.name] = v;
11703 var record = new recordType(values, id);
11705 records[records.length] = record;
11709 totalRecords : records.length
11718 * @class Roo.bootstrap.ComboBox
11719 * @extends Roo.bootstrap.TriggerField
11720 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11721 * @cfg {Boolean} append (true|false) default false
11722 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11723 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11724 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11725 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11726 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11727 * @cfg {Boolean} animate default true
11728 * @cfg {Boolean} emptyResultText only for touch device
11729 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11731 * Create a new ComboBox.
11732 * @param {Object} config Configuration options
11734 Roo.bootstrap.ComboBox = function(config){
11735 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11739 * Fires when the dropdown list is expanded
11740 * @param {Roo.bootstrap.ComboBox} combo This combo box
11745 * Fires when the dropdown list is collapsed
11746 * @param {Roo.bootstrap.ComboBox} combo This combo box
11750 * @event beforeselect
11751 * Fires before a list item is selected. Return false to cancel the selection.
11752 * @param {Roo.bootstrap.ComboBox} combo This combo box
11753 * @param {Roo.data.Record} record The data record returned from the underlying store
11754 * @param {Number} index The index of the selected item in the dropdown list
11756 'beforeselect' : true,
11759 * Fires when a list item is selected
11760 * @param {Roo.bootstrap.ComboBox} combo This combo box
11761 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11762 * @param {Number} index The index of the selected item in the dropdown list
11766 * @event beforequery
11767 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11768 * The event object passed has these properties:
11769 * @param {Roo.bootstrap.ComboBox} combo This combo box
11770 * @param {String} query The query
11771 * @param {Boolean} forceAll true to force "all" query
11772 * @param {Boolean} cancel true to cancel the query
11773 * @param {Object} e The query event object
11775 'beforequery': true,
11778 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11779 * @param {Roo.bootstrap.ComboBox} combo This combo box
11784 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11785 * @param {Roo.bootstrap.ComboBox} combo This combo box
11786 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11791 * Fires when the remove value from the combobox array
11792 * @param {Roo.bootstrap.ComboBox} combo This combo box
11796 * @event afterremove
11797 * Fires when the remove value from the combobox array
11798 * @param {Roo.bootstrap.ComboBox} combo This combo box
11800 'afterremove' : true,
11802 * @event specialfilter
11803 * Fires when specialfilter
11804 * @param {Roo.bootstrap.ComboBox} combo This combo box
11806 'specialfilter' : true,
11809 * Fires when tick the element
11810 * @param {Roo.bootstrap.ComboBox} combo This combo box
11814 * @event touchviewdisplay
11815 * Fires when touch view require special display (default is using displayField)
11816 * @param {Roo.bootstrap.ComboBox} combo This combo box
11817 * @param {Object} cfg set html .
11819 'touchviewdisplay' : true
11824 this.tickItems = [];
11826 this.selectedIndex = -1;
11827 if(this.mode == 'local'){
11828 if(config.queryDelay === undefined){
11829 this.queryDelay = 10;
11831 if(config.minChars === undefined){
11837 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11840 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11841 * rendering into an Roo.Editor, defaults to false)
11844 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11845 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11848 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11851 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11852 * the dropdown list (defaults to undefined, with no header element)
11856 * @cfg {String/Roo.Template} tpl The template to use to render the output
11860 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11862 listWidth: undefined,
11864 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11865 * mode = 'remote' or 'text' if mode = 'local')
11867 displayField: undefined,
11870 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11871 * mode = 'remote' or 'value' if mode = 'local').
11872 * Note: use of a valueField requires the user make a selection
11873 * in order for a value to be mapped.
11875 valueField: undefined,
11879 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11880 * field's data value (defaults to the underlying DOM element's name)
11882 hiddenName: undefined,
11884 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11888 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11890 selectedClass: 'active',
11893 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11897 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11898 * anchor positions (defaults to 'tl-bl')
11900 listAlign: 'tl-bl?',
11902 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11906 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11907 * query specified by the allQuery config option (defaults to 'query')
11909 triggerAction: 'query',
11911 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11912 * (defaults to 4, does not apply if editable = false)
11916 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11917 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11921 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11922 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11926 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11927 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11931 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11932 * when editable = true (defaults to false)
11934 selectOnFocus:false,
11936 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11938 queryParam: 'query',
11940 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
11941 * when mode = 'remote' (defaults to 'Loading...')
11943 loadingText: 'Loading...',
11945 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11949 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11953 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11954 * traditional select (defaults to true)
11958 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11962 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11966 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11967 * listWidth has a higher value)
11971 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11972 * allow the user to set arbitrary text into the field (defaults to false)
11974 forceSelection:false,
11976 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11977 * if typeAhead = true (defaults to 250)
11979 typeAheadDelay : 250,
11981 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11982 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11984 valueNotFoundText : undefined,
11986 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11988 blockFocus : false,
11991 * @cfg {Boolean} disableClear Disable showing of clear button.
11993 disableClear : false,
11995 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
11997 alwaysQuery : false,
12000 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12005 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12007 invalidClass : "has-warning",
12010 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12012 validClass : "has-success",
12015 * @cfg {Boolean} specialFilter (true|false) special filter default false
12017 specialFilter : false,
12020 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12022 mobileTouchView : true,
12034 btnPosition : 'right',
12035 triggerList : true,
12036 showToggleBtn : true,
12038 emptyResultText: 'Empty',
12039 triggerText : 'Select',
12041 // element that contains real text value.. (when hidden is used..)
12043 getAutoCreate : function()
12051 if(Roo.isTouch && this.mobileTouchView){
12052 cfg = this.getAutoCreateTouchView();
12059 if(!this.tickable){
12060 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12065 * ComboBox with tickable selections
12068 var align = this.labelAlign || this.parentLabelAlign();
12071 cls : 'form-group roo-combobox-tickable' //input-group
12076 cls : 'tickable-buttons',
12081 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12082 html : this.triggerText
12088 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12095 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12102 buttons.cn.unshift({
12104 cls: 'roo-select2-search-field-input'
12110 Roo.each(buttons.cn, function(c){
12112 c.cls += ' btn-' + _this.size;
12115 if (_this.disabled) {
12126 cls: 'form-hidden-field'
12130 cls: 'roo-select2-choices',
12134 cls: 'roo-select2-search-field',
12146 cls: 'roo-select2-container input-group roo-select2-container-multi',
12151 // cls: 'typeahead typeahead-long dropdown-menu',
12152 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12157 if(this.hasFeedback && !this.allowBlank){
12161 cls: 'glyphicon form-control-feedback'
12164 combobox.cn.push(feedback);
12167 if (align ==='left' && this.fieldLabel.length) {
12169 // Roo.log("left and has label");
12175 cls : 'control-label col-sm-' + this.labelWidth,
12176 html : this.fieldLabel
12180 cls : "col-sm-" + (12 - this.labelWidth),
12187 } else if ( this.fieldLabel.length) {
12188 // Roo.log(" label");
12193 //cls : 'input-group-addon',
12194 html : this.fieldLabel
12204 // Roo.log(" no label && no align");
12211 ['xs','sm','md','lg'].map(function(size){
12212 if (settings[size]) {
12213 cfg.cls += ' col-' + size + '-' + settings[size];
12221 _initEventsCalled : false,
12224 initEvents: function()
12227 if (this._initEventsCalled) { // as we call render... prevent looping...
12230 this._initEventsCalled = true;
12233 throw "can not find store for combo";
12236 this.store = Roo.factory(this.store, Roo.data);
12238 // if we are building from html. then this element is so complex, that we can not really
12239 // use the rendered HTML.
12240 // so we have to trash and replace the previous code.
12241 if (Roo.XComponent.build_from_html) {
12243 // remove this element....
12244 var e = this.el.dom, k=0;
12245 while (e ) { e = e.previousSibling; ++k;}
12250 this.rendered = false;
12252 this.render(this.parent().getChildContainer(true), k);
12263 if(Roo.isTouch && this.mobileTouchView){
12264 this.initTouchView();
12269 this.initTickableEvents();
12273 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12275 if(this.hiddenName){
12277 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12279 this.hiddenField.dom.value =
12280 this.hiddenValue !== undefined ? this.hiddenValue :
12281 this.value !== undefined ? this.value : '';
12283 // prevent input submission
12284 this.el.dom.removeAttribute('name');
12285 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12290 // this.el.dom.setAttribute('autocomplete', 'off');
12293 var cls = 'x-combo-list';
12295 //this.list = new Roo.Layer({
12296 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12302 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12303 _this.list.setWidth(lw);
12306 this.list.on('mouseover', this.onViewOver, this);
12307 this.list.on('mousemove', this.onViewMove, this);
12309 this.list.on('scroll', this.onViewScroll, this);
12312 this.list.swallowEvent('mousewheel');
12313 this.assetHeight = 0;
12316 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12317 this.assetHeight += this.header.getHeight();
12320 this.innerList = this.list.createChild({cls:cls+'-inner'});
12321 this.innerList.on('mouseover', this.onViewOver, this);
12322 this.innerList.on('mousemove', this.onViewMove, this);
12323 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12325 if(this.allowBlank && !this.pageSize && !this.disableClear){
12326 this.footer = this.list.createChild({cls:cls+'-ft'});
12327 this.pageTb = new Roo.Toolbar(this.footer);
12331 this.footer = this.list.createChild({cls:cls+'-ft'});
12332 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12333 {pageSize: this.pageSize});
12337 if (this.pageTb && this.allowBlank && !this.disableClear) {
12339 this.pageTb.add(new Roo.Toolbar.Fill(), {
12340 cls: 'x-btn-icon x-btn-clear',
12342 handler: function()
12345 _this.clearValue();
12346 _this.onSelect(false, -1);
12351 this.assetHeight += this.footer.getHeight();
12356 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12359 this.view = new Roo.View(this.list, this.tpl, {
12360 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12362 //this.view.wrapEl.setDisplayed(false);
12363 this.view.on('click', this.onViewClick, this);
12367 this.store.on('beforeload', this.onBeforeLoad, this);
12368 this.store.on('load', this.onLoad, this);
12369 this.store.on('loadexception', this.onLoadException, this);
12371 if(this.resizable){
12372 this.resizer = new Roo.Resizable(this.list, {
12373 pinned:true, handles:'se'
12375 this.resizer.on('resize', function(r, w, h){
12376 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12377 this.listWidth = w;
12378 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12379 this.restrictHeight();
12381 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12384 if(!this.editable){
12385 this.editable = true;
12386 this.setEditable(false);
12391 if (typeof(this.events.add.listeners) != 'undefined') {
12393 this.addicon = this.wrap.createChild(
12394 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12396 this.addicon.on('click', function(e) {
12397 this.fireEvent('add', this);
12400 if (typeof(this.events.edit.listeners) != 'undefined') {
12402 this.editicon = this.wrap.createChild(
12403 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12404 if (this.addicon) {
12405 this.editicon.setStyle('margin-left', '40px');
12407 this.editicon.on('click', function(e) {
12409 // we fire even if inothing is selected..
12410 this.fireEvent('edit', this, this.lastData );
12416 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12417 "up" : function(e){
12418 this.inKeyMode = true;
12422 "down" : function(e){
12423 if(!this.isExpanded()){
12424 this.onTriggerClick();
12426 this.inKeyMode = true;
12431 "enter" : function(e){
12432 // this.onViewClick();
12436 if(this.fireEvent("specialkey", this, e)){
12437 this.onViewClick(false);
12443 "esc" : function(e){
12447 "tab" : function(e){
12450 if(this.fireEvent("specialkey", this, e)){
12451 this.onViewClick(false);
12459 doRelay : function(foo, bar, hname){
12460 if(hname == 'down' || this.scope.isExpanded()){
12461 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12470 this.queryDelay = Math.max(this.queryDelay || 10,
12471 this.mode == 'local' ? 10 : 250);
12474 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12476 if(this.typeAhead){
12477 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12479 if(this.editable !== false){
12480 this.inputEl().on("keyup", this.onKeyUp, this);
12482 if(this.forceSelection){
12483 this.inputEl().on('blur', this.doForce, this);
12487 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12488 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12492 initTickableEvents: function()
12496 if(this.hiddenName){
12498 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12500 this.hiddenField.dom.value =
12501 this.hiddenValue !== undefined ? this.hiddenValue :
12502 this.value !== undefined ? this.value : '';
12504 // prevent input submission
12505 this.el.dom.removeAttribute('name');
12506 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12511 // this.list = this.el.select('ul.dropdown-menu',true).first();
12513 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12514 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12515 if(this.triggerList){
12516 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12519 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12520 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12522 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12523 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12525 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12526 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12528 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12529 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12530 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12533 this.cancelBtn.hide();
12538 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12539 _this.list.setWidth(lw);
12542 this.list.on('mouseover', this.onViewOver, this);
12543 this.list.on('mousemove', this.onViewMove, this);
12545 this.list.on('scroll', this.onViewScroll, this);
12548 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>';
12551 this.view = new Roo.View(this.list, this.tpl, {
12552 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12555 //this.view.wrapEl.setDisplayed(false);
12556 this.view.on('click', this.onViewClick, this);
12560 this.store.on('beforeload', this.onBeforeLoad, this);
12561 this.store.on('load', this.onLoad, this);
12562 this.store.on('loadexception', this.onLoadException, this);
12565 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12566 "up" : function(e){
12567 this.inKeyMode = true;
12571 "down" : function(e){
12572 this.inKeyMode = true;
12576 "enter" : function(e){
12577 if(this.fireEvent("specialkey", this, e)){
12578 this.onViewClick(false);
12584 "esc" : function(e){
12585 this.onTickableFooterButtonClick(e, false, false);
12588 "tab" : function(e){
12589 this.fireEvent("specialkey", this, e);
12591 this.onTickableFooterButtonClick(e, false, false);
12598 doRelay : function(e, fn, key){
12599 if(this.scope.isExpanded()){
12600 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12609 this.queryDelay = Math.max(this.queryDelay || 10,
12610 this.mode == 'local' ? 10 : 250);
12613 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12615 if(this.typeAhead){
12616 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12619 if(this.editable !== false){
12620 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12625 onDestroy : function(){
12627 this.view.setStore(null);
12628 this.view.el.removeAllListeners();
12629 this.view.el.remove();
12630 this.view.purgeListeners();
12633 this.list.dom.innerHTML = '';
12637 this.store.un('beforeload', this.onBeforeLoad, this);
12638 this.store.un('load', this.onLoad, this);
12639 this.store.un('loadexception', this.onLoadException, this);
12641 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12645 fireKey : function(e){
12646 if(e.isNavKeyPress() && !this.list.isVisible()){
12647 this.fireEvent("specialkey", this, e);
12652 onResize: function(w, h){
12653 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12655 // if(typeof w != 'number'){
12656 // // we do not handle it!?!?
12659 // var tw = this.trigger.getWidth();
12660 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12661 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12663 // this.inputEl().setWidth( this.adjustWidth('input', x));
12665 // //this.trigger.setStyle('left', x+'px');
12667 // if(this.list && this.listWidth === undefined){
12668 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12669 // this.list.setWidth(lw);
12670 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12678 * Allow or prevent the user from directly editing the field text. If false is passed,
12679 * the user will only be able to select from the items defined in the dropdown list. This method
12680 * is the runtime equivalent of setting the 'editable' config option at config time.
12681 * @param {Boolean} value True to allow the user to directly edit the field text
12683 setEditable : function(value){
12684 if(value == this.editable){
12687 this.editable = value;
12689 this.inputEl().dom.setAttribute('readOnly', true);
12690 this.inputEl().on('mousedown', this.onTriggerClick, this);
12691 this.inputEl().addClass('x-combo-noedit');
12693 this.inputEl().dom.setAttribute('readOnly', false);
12694 this.inputEl().un('mousedown', this.onTriggerClick, this);
12695 this.inputEl().removeClass('x-combo-noedit');
12701 onBeforeLoad : function(combo,opts){
12702 if(!this.hasFocus){
12706 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12708 this.restrictHeight();
12709 this.selectedIndex = -1;
12713 onLoad : function(){
12715 this.hasQuery = false;
12717 if(!this.hasFocus){
12721 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12722 this.loading.hide();
12725 if(this.store.getCount() > 0){
12727 this.restrictHeight();
12728 if(this.lastQuery == this.allQuery){
12729 if(this.editable && !this.tickable){
12730 this.inputEl().dom.select();
12734 !this.selectByValue(this.value, true) &&
12737 !this.store.lastOptions ||
12738 typeof(this.store.lastOptions.add) == 'undefined' ||
12739 this.store.lastOptions.add != true
12742 this.select(0, true);
12745 if(this.autoFocus){
12748 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12749 this.taTask.delay(this.typeAheadDelay);
12753 this.onEmptyResults();
12759 onLoadException : function()
12761 this.hasQuery = false;
12763 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12764 this.loading.hide();
12767 if(this.tickable && this.editable){
12772 // only causes errors at present
12773 //Roo.log(this.store.reader.jsonData);
12774 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12776 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12782 onTypeAhead : function(){
12783 if(this.store.getCount() > 0){
12784 var r = this.store.getAt(0);
12785 var newValue = r.data[this.displayField];
12786 var len = newValue.length;
12787 var selStart = this.getRawValue().length;
12789 if(selStart != len){
12790 this.setRawValue(newValue);
12791 this.selectText(selStart, newValue.length);
12797 onSelect : function(record, index){
12799 if(this.fireEvent('beforeselect', this, record, index) !== false){
12801 this.setFromData(index > -1 ? record.data : false);
12804 this.fireEvent('select', this, record, index);
12809 * Returns the currently selected field value or empty string if no value is set.
12810 * @return {String} value The selected value
12812 getValue : function(){
12815 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12818 if(this.valueField){
12819 return typeof this.value != 'undefined' ? this.value : '';
12821 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12826 * Clears any text/value currently set in the field
12828 clearValue : function(){
12829 if(this.hiddenField){
12830 this.hiddenField.dom.value = '';
12833 this.setRawValue('');
12834 this.lastSelectionText = '';
12835 this.lastData = false;
12837 var close = this.closeTriggerEl();
12846 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12847 * will be displayed in the field. If the value does not match the data value of an existing item,
12848 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12849 * Otherwise the field will be blank (although the value will still be set).
12850 * @param {String} value The value to match
12852 setValue : function(v){
12859 if(this.valueField){
12860 var r = this.findRecord(this.valueField, v);
12862 text = r.data[this.displayField];
12863 }else if(this.valueNotFoundText !== undefined){
12864 text = this.valueNotFoundText;
12867 this.lastSelectionText = text;
12868 if(this.hiddenField){
12869 this.hiddenField.dom.value = v;
12871 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12874 var close = this.closeTriggerEl();
12877 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12881 * @property {Object} the last set data for the element
12886 * Sets the value of the field based on a object which is related to the record format for the store.
12887 * @param {Object} value the value to set as. or false on reset?
12889 setFromData : function(o){
12896 var dv = ''; // display value
12897 var vv = ''; // value value..
12899 if (this.displayField) {
12900 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12902 // this is an error condition!!!
12903 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12906 if(this.valueField){
12907 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12910 var close = this.closeTriggerEl();
12913 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12916 if(this.hiddenField){
12917 this.hiddenField.dom.value = vv;
12919 this.lastSelectionText = dv;
12920 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12924 // no hidden field.. - we store the value in 'value', but still display
12925 // display field!!!!
12926 this.lastSelectionText = dv;
12927 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12934 reset : function(){
12935 // overridden so that last data is reset..
12942 this.setValue(this.originalValue);
12943 this.clearInvalid();
12944 this.lastData = false;
12946 this.view.clearSelections();
12950 findRecord : function(prop, value){
12952 if(this.store.getCount() > 0){
12953 this.store.each(function(r){
12954 if(r.data[prop] == value){
12964 getName: function()
12966 // returns hidden if it's set..
12967 if (!this.rendered) {return ''};
12968 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
12972 onViewMove : function(e, t){
12973 this.inKeyMode = false;
12977 onViewOver : function(e, t){
12978 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12981 var item = this.view.findItemFromChild(t);
12984 var index = this.view.indexOf(item);
12985 this.select(index, false);
12990 onViewClick : function(view, doFocus, el, e)
12992 var index = this.view.getSelectedIndexes()[0];
12994 var r = this.store.getAt(index);
12998 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13005 Roo.each(this.tickItems, function(v,k){
13007 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13009 _this.tickItems.splice(k, 1);
13011 if(typeof(e) == 'undefined' && view == false){
13012 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13024 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13025 this.tickItems.push(r.data);
13028 if(typeof(e) == 'undefined' && view == false){
13029 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13036 this.onSelect(r, index);
13038 if(doFocus !== false && !this.blockFocus){
13039 this.inputEl().focus();
13044 restrictHeight : function(){
13045 //this.innerList.dom.style.height = '';
13046 //var inner = this.innerList.dom;
13047 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13048 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13049 //this.list.beginUpdate();
13050 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13051 this.list.alignTo(this.inputEl(), this.listAlign);
13052 this.list.alignTo(this.inputEl(), this.listAlign);
13053 //this.list.endUpdate();
13057 onEmptyResults : function(){
13059 if(this.tickable && this.editable){
13060 this.restrictHeight();
13068 * Returns true if the dropdown list is expanded, else false.
13070 isExpanded : function(){
13071 return this.list.isVisible();
13075 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13076 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13077 * @param {String} value The data value of the item to select
13078 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13079 * selected item if it is not currently in view (defaults to true)
13080 * @return {Boolean} True if the value matched an item in the list, else false
13082 selectByValue : function(v, scrollIntoView){
13083 if(v !== undefined && v !== null){
13084 var r = this.findRecord(this.valueField || this.displayField, v);
13086 this.select(this.store.indexOf(r), scrollIntoView);
13094 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13095 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13096 * @param {Number} index The zero-based index of the list item to select
13097 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13098 * selected item if it is not currently in view (defaults to true)
13100 select : function(index, scrollIntoView){
13101 this.selectedIndex = index;
13102 this.view.select(index);
13103 if(scrollIntoView !== false){
13104 var el = this.view.getNode(index);
13106 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13109 this.list.scrollChildIntoView(el, false);
13115 selectNext : function(){
13116 var ct = this.store.getCount();
13118 if(this.selectedIndex == -1){
13120 }else if(this.selectedIndex < ct-1){
13121 this.select(this.selectedIndex+1);
13127 selectPrev : function(){
13128 var ct = this.store.getCount();
13130 if(this.selectedIndex == -1){
13132 }else if(this.selectedIndex != 0){
13133 this.select(this.selectedIndex-1);
13139 onKeyUp : function(e){
13140 if(this.editable !== false && !e.isSpecialKey()){
13141 this.lastKey = e.getKey();
13142 this.dqTask.delay(this.queryDelay);
13147 validateBlur : function(){
13148 return !this.list || !this.list.isVisible();
13152 initQuery : function(){
13154 var v = this.getRawValue();
13156 if(this.tickable && this.editable){
13157 v = this.tickableInputEl().getValue();
13164 doForce : function(){
13165 if(this.inputEl().dom.value.length > 0){
13166 this.inputEl().dom.value =
13167 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13173 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13174 * query allowing the query action to be canceled if needed.
13175 * @param {String} query The SQL query to execute
13176 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13177 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13178 * saved in the current store (defaults to false)
13180 doQuery : function(q, forceAll){
13182 if(q === undefined || q === null){
13187 forceAll: forceAll,
13191 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13196 forceAll = qe.forceAll;
13197 if(forceAll === true || (q.length >= this.minChars)){
13199 this.hasQuery = true;
13201 if(this.lastQuery != q || this.alwaysQuery){
13202 this.lastQuery = q;
13203 if(this.mode == 'local'){
13204 this.selectedIndex = -1;
13206 this.store.clearFilter();
13209 if(this.specialFilter){
13210 this.fireEvent('specialfilter', this);
13215 this.store.filter(this.displayField, q);
13218 this.store.fireEvent("datachanged", this.store);
13225 this.store.baseParams[this.queryParam] = q;
13227 var options = {params : this.getParams(q)};
13230 options.add = true;
13231 options.params.start = this.page * this.pageSize;
13234 this.store.load(options);
13237 * this code will make the page width larger, at the beginning, the list not align correctly,
13238 * we should expand the list on onLoad
13239 * so command out it
13244 this.selectedIndex = -1;
13249 this.loadNext = false;
13253 getParams : function(q){
13255 //p[this.queryParam] = q;
13259 p.limit = this.pageSize;
13265 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13267 collapse : function(){
13268 if(!this.isExpanded()){
13275 this.hasFocus = false;
13277 this.cancelBtn.hide();
13278 this.trigger.show();
13281 this.tickableInputEl().dom.value = '';
13282 this.tickableInputEl().blur();
13287 Roo.get(document).un('mousedown', this.collapseIf, this);
13288 Roo.get(document).un('mousewheel', this.collapseIf, this);
13289 if (!this.editable) {
13290 Roo.get(document).un('keydown', this.listKeyPress, this);
13292 this.fireEvent('collapse', this);
13296 collapseIf : function(e){
13297 var in_combo = e.within(this.el);
13298 var in_list = e.within(this.list);
13299 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13301 if (in_combo || in_list || is_list) {
13302 //e.stopPropagation();
13307 this.onTickableFooterButtonClick(e, false, false);
13315 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13317 expand : function(){
13319 if(this.isExpanded() || !this.hasFocus){
13323 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13324 this.list.setWidth(lw);
13331 this.restrictHeight();
13335 this.tickItems = Roo.apply([], this.item);
13338 this.cancelBtn.show();
13339 this.trigger.hide();
13342 this.tickableInputEl().focus();
13347 Roo.get(document).on('mousedown', this.collapseIf, this);
13348 Roo.get(document).on('mousewheel', this.collapseIf, this);
13349 if (!this.editable) {
13350 Roo.get(document).on('keydown', this.listKeyPress, this);
13353 this.fireEvent('expand', this);
13357 // Implements the default empty TriggerField.onTriggerClick function
13358 onTriggerClick : function(e)
13360 Roo.log('trigger click');
13362 if(this.disabled || !this.triggerList){
13367 this.loadNext = false;
13369 if(this.isExpanded()){
13371 if (!this.blockFocus) {
13372 this.inputEl().focus();
13376 this.hasFocus = true;
13377 if(this.triggerAction == 'all') {
13378 this.doQuery(this.allQuery, true);
13380 this.doQuery(this.getRawValue());
13382 if (!this.blockFocus) {
13383 this.inputEl().focus();
13388 onTickableTriggerClick : function(e)
13395 this.loadNext = false;
13396 this.hasFocus = true;
13398 if(this.triggerAction == 'all') {
13399 this.doQuery(this.allQuery, true);
13401 this.doQuery(this.getRawValue());
13405 onSearchFieldClick : function(e)
13407 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13408 this.onTickableFooterButtonClick(e, false, false);
13412 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13417 this.loadNext = false;
13418 this.hasFocus = true;
13420 if(this.triggerAction == 'all') {
13421 this.doQuery(this.allQuery, true);
13423 this.doQuery(this.getRawValue());
13427 listKeyPress : function(e)
13429 //Roo.log('listkeypress');
13430 // scroll to first matching element based on key pres..
13431 if (e.isSpecialKey()) {
13434 var k = String.fromCharCode(e.getKey()).toUpperCase();
13437 var csel = this.view.getSelectedNodes();
13438 var cselitem = false;
13440 var ix = this.view.indexOf(csel[0]);
13441 cselitem = this.store.getAt(ix);
13442 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13448 this.store.each(function(v) {
13450 // start at existing selection.
13451 if (cselitem.id == v.id) {
13457 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13458 match = this.store.indexOf(v);
13464 if (match === false) {
13465 return true; // no more action?
13468 this.view.select(match);
13469 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13470 sn.scrollIntoView(sn.dom.parentNode, false);
13473 onViewScroll : function(e, t){
13475 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){
13479 this.hasQuery = true;
13481 this.loading = this.list.select('.loading', true).first();
13483 if(this.loading === null){
13484 this.list.createChild({
13486 cls: 'loading roo-select2-more-results roo-select2-active',
13487 html: 'Loading more results...'
13490 this.loading = this.list.select('.loading', true).first();
13492 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13494 this.loading.hide();
13497 this.loading.show();
13502 this.loadNext = true;
13504 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13509 addItem : function(o)
13511 var dv = ''; // display value
13513 if (this.displayField) {
13514 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13516 // this is an error condition!!!
13517 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13524 var choice = this.choices.createChild({
13526 cls: 'roo-select2-search-choice',
13535 cls: 'roo-select2-search-choice-close',
13540 }, this.searchField);
13542 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13544 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13552 this.inputEl().dom.value = '';
13557 onRemoveItem : function(e, _self, o)
13559 e.preventDefault();
13561 this.lastItem = Roo.apply([], this.item);
13563 var index = this.item.indexOf(o.data) * 1;
13566 Roo.log('not this item?!');
13570 this.item.splice(index, 1);
13575 this.fireEvent('remove', this, e);
13581 syncValue : function()
13583 if(!this.item.length){
13590 Roo.each(this.item, function(i){
13591 if(_this.valueField){
13592 value.push(i[_this.valueField]);
13599 this.value = value.join(',');
13601 if(this.hiddenField){
13602 this.hiddenField.dom.value = this.value;
13605 this.store.fireEvent("datachanged", this.store);
13608 clearItem : function()
13610 if(!this.multiple){
13616 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13624 if(this.tickable && !Roo.isTouch){
13625 this.view.refresh();
13629 inputEl: function ()
13631 if(Roo.isTouch && this.mobileTouchView){
13632 return this.el.select('input.form-control',true).first();
13636 return this.searchField;
13639 return this.el.select('input.form-control',true).first();
13643 onTickableFooterButtonClick : function(e, btn, el)
13645 e.preventDefault();
13647 this.lastItem = Roo.apply([], this.item);
13649 if(btn && btn.name == 'cancel'){
13650 this.tickItems = Roo.apply([], this.item);
13659 Roo.each(this.tickItems, function(o){
13667 validate : function()
13669 var v = this.getRawValue();
13672 v = this.getValue();
13675 if(this.disabled || this.allowBlank || v.length){
13680 this.markInvalid();
13684 tickableInputEl : function()
13686 if(!this.tickable || !this.editable){
13687 return this.inputEl();
13690 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13694 getAutoCreateTouchView : function()
13699 cls: 'form-group' //input-group
13705 type : this.inputType,
13706 cls : 'form-control x-combo-noedit',
13707 autocomplete: 'new-password',
13708 placeholder : this.placeholder || '',
13713 input.name = this.name;
13717 input.cls += ' input-' + this.size;
13720 if (this.disabled) {
13721 input.disabled = true;
13732 inputblock.cls += ' input-group';
13734 inputblock.cn.unshift({
13736 cls : 'input-group-addon',
13741 if(this.removable && !this.multiple){
13742 inputblock.cls += ' roo-removable';
13744 inputblock.cn.push({
13747 cls : 'roo-combo-removable-btn close'
13751 if(this.hasFeedback && !this.allowBlank){
13753 inputblock.cls += ' has-feedback';
13755 inputblock.cn.push({
13757 cls: 'glyphicon form-control-feedback'
13764 inputblock.cls += (this.before) ? '' : ' input-group';
13766 inputblock.cn.push({
13768 cls : 'input-group-addon',
13779 cls: 'form-hidden-field'
13793 cls: 'form-hidden-field'
13797 cls: 'roo-select2-choices',
13801 cls: 'roo-select2-search-field',
13814 cls: 'roo-select2-container input-group',
13821 combobox.cls += ' roo-select2-container-multi';
13824 var align = this.labelAlign || this.parentLabelAlign();
13828 if(this.fieldLabel.length){
13830 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13831 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13836 cls : 'control-label ' + lw,
13837 html : this.fieldLabel
13849 var settings = this;
13851 ['xs','sm','md','lg'].map(function(size){
13852 if (settings[size]) {
13853 cfg.cls += ' col-' + size + '-' + settings[size];
13860 initTouchView : function()
13862 this.renderTouchView();
13864 this.touchViewEl.on('scroll', function(){
13865 this.el.dom.scrollTop = 0;
13868 this.originalValue = this.getValue();
13870 this.inputEl().on("click", this.showTouchView, this);
13872 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13873 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13875 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13877 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13878 this.store.on('load', this.onTouchViewLoad, this);
13879 this.store.on('loadexception', this.onTouchViewLoadException, this);
13881 if(this.hiddenName){
13883 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13885 this.hiddenField.dom.value =
13886 this.hiddenValue !== undefined ? this.hiddenValue :
13887 this.value !== undefined ? this.value : '';
13889 this.el.dom.removeAttribute('name');
13890 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13894 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13895 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13898 if(this.removable && !this.multiple){
13899 var close = this.closeTriggerEl();
13901 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13902 close.on('click', this.removeBtnClick, this, close);
13906 * fix the bug in Safari iOS8
13908 this.inputEl().on("focus", function(e){
13909 document.activeElement.blur();
13917 renderTouchView : function()
13919 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13920 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13922 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13923 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13925 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13926 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13927 this.touchViewBodyEl.setStyle('overflow', 'auto');
13929 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13930 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13932 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13933 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13937 showTouchView : function()
13943 this.touchViewHeaderEl.hide();
13945 if(this.fieldLabel.length){
13946 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13947 this.touchViewHeaderEl.show();
13950 this.touchViewEl.show();
13952 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13953 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13955 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13957 if(this.fieldLabel.length){
13958 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13961 this.touchViewBodyEl.setHeight(bodyHeight);
13965 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13967 this.touchViewEl.addClass('in');
13970 this.doTouchViewQuery();
13974 hideTouchView : function()
13976 this.touchViewEl.removeClass('in');
13980 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13982 this.touchViewEl.setStyle('display', 'none');
13987 setTouchViewValue : function()
13994 Roo.each(this.tickItems, function(o){
13999 this.hideTouchView();
14002 doTouchViewQuery : function()
14011 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14015 if(!this.alwaysQuery || this.mode == 'local'){
14016 this.onTouchViewLoad();
14023 onTouchViewBeforeLoad : function(combo,opts)
14029 onTouchViewLoad : function()
14031 if(this.store.getCount() < 1){
14032 this.onTouchViewEmptyResults();
14036 this.clearTouchView();
14038 var rawValue = this.getRawValue();
14040 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14042 this.tickItems = [];
14044 this.store.data.each(function(d, rowIndex){
14045 var row = this.touchViewListGroup.createChild(template);
14047 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14048 row.addClass(d.data.cls);
14051 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14054 html : d.data[this.displayField]
14057 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14058 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14062 if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
14063 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14066 if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
14067 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14068 this.tickItems.push(d.data);
14071 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14075 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14077 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14079 if(this.fieldLabel.length){
14080 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14083 var listHeight = this.touchViewListGroup.getHeight();
14087 if(firstChecked && listHeight > bodyHeight){
14088 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14093 onTouchViewLoadException : function()
14095 this.hideTouchView();
14098 onTouchViewEmptyResults : function()
14100 this.clearTouchView();
14102 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14104 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14108 clearTouchView : function()
14110 this.touchViewListGroup.dom.innerHTML = '';
14113 onTouchViewClick : function(e, el, o)
14115 e.preventDefault();
14118 var rowIndex = o.rowIndex;
14120 var r = this.store.getAt(rowIndex);
14122 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14124 if(!this.multiple){
14125 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14126 c.dom.removeAttribute('checked');
14129 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14131 this.setFromData(r.data);
14133 var close = this.closeTriggerEl();
14139 this.hideTouchView();
14141 this.fireEvent('select', this, r, rowIndex);
14146 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14147 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14148 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14152 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14153 this.addItem(r.data);
14154 this.tickItems.push(r.data);
14160 * @cfg {Boolean} grow
14164 * @cfg {Number} growMin
14168 * @cfg {Number} growMax
14177 Roo.apply(Roo.bootstrap.ComboBox, {
14181 cls: 'modal-header',
14203 cls: 'list-group-item',
14207 cls: 'roo-combobox-list-group-item-value'
14211 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14225 listItemCheckbox : {
14227 cls: 'list-group-item',
14231 cls: 'roo-combobox-list-group-item-value'
14235 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14251 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14256 cls: 'modal-footer',
14264 cls: 'col-xs-6 text-left',
14267 cls: 'btn btn-danger roo-touch-view-cancel',
14273 cls: 'col-xs-6 text-right',
14276 cls: 'btn btn-success roo-touch-view-ok',
14287 Roo.apply(Roo.bootstrap.ComboBox, {
14289 touchViewTemplate : {
14291 cls: 'modal fade roo-combobox-touch-view',
14295 cls: 'modal-dialog',
14296 style : 'position:fixed', // we have to fix position....
14300 cls: 'modal-content',
14302 Roo.bootstrap.ComboBox.header,
14303 Roo.bootstrap.ComboBox.body,
14304 Roo.bootstrap.ComboBox.footer
14313 * Ext JS Library 1.1.1
14314 * Copyright(c) 2006-2007, Ext JS, LLC.
14316 * Originally Released Under LGPL - original licence link has changed is not relivant.
14319 * <script type="text/javascript">
14324 * @extends Roo.util.Observable
14325 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14326 * This class also supports single and multi selection modes. <br>
14327 * Create a data model bound view:
14329 var store = new Roo.data.Store(...);
14331 var view = new Roo.View({
14333 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14335 singleSelect: true,
14336 selectedClass: "ydataview-selected",
14340 // listen for node click?
14341 view.on("click", function(vw, index, node, e){
14342 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14346 dataModel.load("foobar.xml");
14348 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14350 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14351 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14353 * Note: old style constructor is still suported (container, template, config)
14356 * Create a new View
14357 * @param {Object} config The config object
14360 Roo.View = function(config, depreciated_tpl, depreciated_config){
14362 this.parent = false;
14364 if (typeof(depreciated_tpl) == 'undefined') {
14365 // new way.. - universal constructor.
14366 Roo.apply(this, config);
14367 this.el = Roo.get(this.el);
14370 this.el = Roo.get(config);
14371 this.tpl = depreciated_tpl;
14372 Roo.apply(this, depreciated_config);
14374 this.wrapEl = this.el.wrap().wrap();
14375 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14378 if(typeof(this.tpl) == "string"){
14379 this.tpl = new Roo.Template(this.tpl);
14381 // support xtype ctors..
14382 this.tpl = new Roo.factory(this.tpl, Roo);
14386 this.tpl.compile();
14391 * @event beforeclick
14392 * Fires before a click is processed. Returns false to cancel the default action.
14393 * @param {Roo.View} this
14394 * @param {Number} index The index of the target node
14395 * @param {HTMLElement} node The target node
14396 * @param {Roo.EventObject} e The raw event object
14398 "beforeclick" : true,
14401 * Fires when a template node is clicked.
14402 * @param {Roo.View} this
14403 * @param {Number} index The index of the target node
14404 * @param {HTMLElement} node The target node
14405 * @param {Roo.EventObject} e The raw event object
14410 * Fires when a template node is double clicked.
14411 * @param {Roo.View} this
14412 * @param {Number} index The index of the target node
14413 * @param {HTMLElement} node The target node
14414 * @param {Roo.EventObject} e The raw event object
14418 * @event contextmenu
14419 * Fires when a template node is right clicked.
14420 * @param {Roo.View} this
14421 * @param {Number} index The index of the target node
14422 * @param {HTMLElement} node The target node
14423 * @param {Roo.EventObject} e The raw event object
14425 "contextmenu" : true,
14427 * @event selectionchange
14428 * Fires when the selected nodes change.
14429 * @param {Roo.View} this
14430 * @param {Array} selections Array of the selected nodes
14432 "selectionchange" : true,
14435 * @event beforeselect
14436 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14437 * @param {Roo.View} this
14438 * @param {HTMLElement} node The node to be selected
14439 * @param {Array} selections Array of currently selected nodes
14441 "beforeselect" : true,
14443 * @event preparedata
14444 * Fires on every row to render, to allow you to change the data.
14445 * @param {Roo.View} this
14446 * @param {Object} data to be rendered (change this)
14448 "preparedata" : true
14456 "click": this.onClick,
14457 "dblclick": this.onDblClick,
14458 "contextmenu": this.onContextMenu,
14462 this.selections = [];
14464 this.cmp = new Roo.CompositeElementLite([]);
14466 this.store = Roo.factory(this.store, Roo.data);
14467 this.setStore(this.store, true);
14470 if ( this.footer && this.footer.xtype) {
14472 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14474 this.footer.dataSource = this.store;
14475 this.footer.container = fctr;
14476 this.footer = Roo.factory(this.footer, Roo);
14477 fctr.insertFirst(this.el);
14479 // this is a bit insane - as the paging toolbar seems to detach the el..
14480 // dom.parentNode.parentNode.parentNode
14481 // they get detached?
14485 Roo.View.superclass.constructor.call(this);
14490 Roo.extend(Roo.View, Roo.util.Observable, {
14493 * @cfg {Roo.data.Store} store Data store to load data from.
14498 * @cfg {String|Roo.Element} el The container element.
14503 * @cfg {String|Roo.Template} tpl The template used by this View
14507 * @cfg {String} dataName the named area of the template to use as the data area
14508 * Works with domtemplates roo-name="name"
14512 * @cfg {String} selectedClass The css class to add to selected nodes
14514 selectedClass : "x-view-selected",
14516 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14521 * @cfg {String} text to display on mask (default Loading)
14525 * @cfg {Boolean} multiSelect Allow multiple selection
14527 multiSelect : false,
14529 * @cfg {Boolean} singleSelect Allow single selection
14531 singleSelect: false,
14534 * @cfg {Boolean} toggleSelect - selecting
14536 toggleSelect : false,
14539 * @cfg {Boolean} tickable - selecting
14544 * Returns the element this view is bound to.
14545 * @return {Roo.Element}
14547 getEl : function(){
14548 return this.wrapEl;
14554 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14556 refresh : function(){
14557 //Roo.log('refresh');
14560 // if we are using something like 'domtemplate', then
14561 // the what gets used is:
14562 // t.applySubtemplate(NAME, data, wrapping data..)
14563 // the outer template then get' applied with
14564 // the store 'extra data'
14565 // and the body get's added to the
14566 // roo-name="data" node?
14567 // <span class='roo-tpl-{name}'></span> ?????
14571 this.clearSelections();
14572 this.el.update("");
14574 var records = this.store.getRange();
14575 if(records.length < 1) {
14577 // is this valid?? = should it render a template??
14579 this.el.update(this.emptyText);
14583 if (this.dataName) {
14584 this.el.update(t.apply(this.store.meta)); //????
14585 el = this.el.child('.roo-tpl-' + this.dataName);
14588 for(var i = 0, len = records.length; i < len; i++){
14589 var data = this.prepareData(records[i].data, i, records[i]);
14590 this.fireEvent("preparedata", this, data, i, records[i]);
14592 var d = Roo.apply({}, data);
14595 Roo.apply(d, {'roo-id' : Roo.id()});
14599 Roo.each(this.parent.item, function(item){
14600 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14603 Roo.apply(d, {'roo-data-checked' : 'checked'});
14607 html[html.length] = Roo.util.Format.trim(
14609 t.applySubtemplate(this.dataName, d, this.store.meta) :
14616 el.update(html.join(""));
14617 this.nodes = el.dom.childNodes;
14618 this.updateIndexes(0);
14623 * Function to override to reformat the data that is sent to
14624 * the template for each node.
14625 * DEPRICATED - use the preparedata event handler.
14626 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14627 * a JSON object for an UpdateManager bound view).
14629 prepareData : function(data, index, record)
14631 this.fireEvent("preparedata", this, data, index, record);
14635 onUpdate : function(ds, record){
14636 // Roo.log('on update');
14637 this.clearSelections();
14638 var index = this.store.indexOf(record);
14639 var n = this.nodes[index];
14640 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14641 n.parentNode.removeChild(n);
14642 this.updateIndexes(index, index);
14648 onAdd : function(ds, records, index)
14650 //Roo.log(['on Add', ds, records, index] );
14651 this.clearSelections();
14652 if(this.nodes.length == 0){
14656 var n = this.nodes[index];
14657 for(var i = 0, len = records.length; i < len; i++){
14658 var d = this.prepareData(records[i].data, i, records[i]);
14660 this.tpl.insertBefore(n, d);
14663 this.tpl.append(this.el, d);
14666 this.updateIndexes(index);
14669 onRemove : function(ds, record, index){
14670 // Roo.log('onRemove');
14671 this.clearSelections();
14672 var el = this.dataName ?
14673 this.el.child('.roo-tpl-' + this.dataName) :
14676 el.dom.removeChild(this.nodes[index]);
14677 this.updateIndexes(index);
14681 * Refresh an individual node.
14682 * @param {Number} index
14684 refreshNode : function(index){
14685 this.onUpdate(this.store, this.store.getAt(index));
14688 updateIndexes : function(startIndex, endIndex){
14689 var ns = this.nodes;
14690 startIndex = startIndex || 0;
14691 endIndex = endIndex || ns.length - 1;
14692 for(var i = startIndex; i <= endIndex; i++){
14693 ns[i].nodeIndex = i;
14698 * Changes the data store this view uses and refresh the view.
14699 * @param {Store} store
14701 setStore : function(store, initial){
14702 if(!initial && this.store){
14703 this.store.un("datachanged", this.refresh);
14704 this.store.un("add", this.onAdd);
14705 this.store.un("remove", this.onRemove);
14706 this.store.un("update", this.onUpdate);
14707 this.store.un("clear", this.refresh);
14708 this.store.un("beforeload", this.onBeforeLoad);
14709 this.store.un("load", this.onLoad);
14710 this.store.un("loadexception", this.onLoad);
14714 store.on("datachanged", this.refresh, this);
14715 store.on("add", this.onAdd, this);
14716 store.on("remove", this.onRemove, this);
14717 store.on("update", this.onUpdate, this);
14718 store.on("clear", this.refresh, this);
14719 store.on("beforeload", this.onBeforeLoad, this);
14720 store.on("load", this.onLoad, this);
14721 store.on("loadexception", this.onLoad, this);
14729 * onbeforeLoad - masks the loading area.
14732 onBeforeLoad : function(store,opts)
14734 //Roo.log('onBeforeLoad');
14736 this.el.update("");
14738 this.el.mask(this.mask ? this.mask : "Loading" );
14740 onLoad : function ()
14747 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14748 * @param {HTMLElement} node
14749 * @return {HTMLElement} The template node
14751 findItemFromChild : function(node){
14752 var el = this.dataName ?
14753 this.el.child('.roo-tpl-' + this.dataName,true) :
14756 if(!node || node.parentNode == el){
14759 var p = node.parentNode;
14760 while(p && p != el){
14761 if(p.parentNode == el){
14770 onClick : function(e){
14771 var item = this.findItemFromChild(e.getTarget());
14773 var index = this.indexOf(item);
14774 if(this.onItemClick(item, index, e) !== false){
14775 this.fireEvent("click", this, index, item, e);
14778 this.clearSelections();
14783 onContextMenu : function(e){
14784 var item = this.findItemFromChild(e.getTarget());
14786 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14791 onDblClick : function(e){
14792 var item = this.findItemFromChild(e.getTarget());
14794 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14798 onItemClick : function(item, index, e)
14800 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14803 if (this.toggleSelect) {
14804 var m = this.isSelected(item) ? 'unselect' : 'select';
14807 _t[m](item, true, false);
14810 if(this.multiSelect || this.singleSelect){
14811 if(this.multiSelect && e.shiftKey && this.lastSelection){
14812 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14814 this.select(item, this.multiSelect && e.ctrlKey);
14815 this.lastSelection = item;
14818 if(!this.tickable){
14819 e.preventDefault();
14827 * Get the number of selected nodes.
14830 getSelectionCount : function(){
14831 return this.selections.length;
14835 * Get the currently selected nodes.
14836 * @return {Array} An array of HTMLElements
14838 getSelectedNodes : function(){
14839 return this.selections;
14843 * Get the indexes of the selected nodes.
14846 getSelectedIndexes : function(){
14847 var indexes = [], s = this.selections;
14848 for(var i = 0, len = s.length; i < len; i++){
14849 indexes.push(s[i].nodeIndex);
14855 * Clear all selections
14856 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14858 clearSelections : function(suppressEvent){
14859 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14860 this.cmp.elements = this.selections;
14861 this.cmp.removeClass(this.selectedClass);
14862 this.selections = [];
14863 if(!suppressEvent){
14864 this.fireEvent("selectionchange", this, this.selections);
14870 * Returns true if the passed node is selected
14871 * @param {HTMLElement/Number} node The node or node index
14872 * @return {Boolean}
14874 isSelected : function(node){
14875 var s = this.selections;
14879 node = this.getNode(node);
14880 return s.indexOf(node) !== -1;
14885 * @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
14886 * @param {Boolean} keepExisting (optional) true to keep existing selections
14887 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14889 select : function(nodeInfo, keepExisting, suppressEvent){
14890 if(nodeInfo instanceof Array){
14892 this.clearSelections(true);
14894 for(var i = 0, len = nodeInfo.length; i < len; i++){
14895 this.select(nodeInfo[i], true, true);
14899 var node = this.getNode(nodeInfo);
14900 if(!node || this.isSelected(node)){
14901 return; // already selected.
14904 this.clearSelections(true);
14907 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14908 Roo.fly(node).addClass(this.selectedClass);
14909 this.selections.push(node);
14910 if(!suppressEvent){
14911 this.fireEvent("selectionchange", this, this.selections);
14919 * @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
14920 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14921 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14923 unselect : function(nodeInfo, keepExisting, suppressEvent)
14925 if(nodeInfo instanceof Array){
14926 Roo.each(this.selections, function(s) {
14927 this.unselect(s, nodeInfo);
14931 var node = this.getNode(nodeInfo);
14932 if(!node || !this.isSelected(node)){
14933 //Roo.log("not selected");
14934 return; // not selected.
14938 Roo.each(this.selections, function(s) {
14940 Roo.fly(node).removeClass(this.selectedClass);
14947 this.selections= ns;
14948 this.fireEvent("selectionchange", this, this.selections);
14952 * Gets a template node.
14953 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14954 * @return {HTMLElement} The node or null if it wasn't found
14956 getNode : function(nodeInfo){
14957 if(typeof nodeInfo == "string"){
14958 return document.getElementById(nodeInfo);
14959 }else if(typeof nodeInfo == "number"){
14960 return this.nodes[nodeInfo];
14966 * Gets a range template nodes.
14967 * @param {Number} startIndex
14968 * @param {Number} endIndex
14969 * @return {Array} An array of nodes
14971 getNodes : function(start, end){
14972 var ns = this.nodes;
14973 start = start || 0;
14974 end = typeof end == "undefined" ? ns.length - 1 : end;
14977 for(var i = start; i <= end; i++){
14981 for(var i = start; i >= end; i--){
14989 * Finds the index of the passed node
14990 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14991 * @return {Number} The index of the node or -1
14993 indexOf : function(node){
14994 node = this.getNode(node);
14995 if(typeof node.nodeIndex == "number"){
14996 return node.nodeIndex;
14998 var ns = this.nodes;
14999 for(var i = 0, len = ns.length; i < len; i++){
15010 * based on jquery fullcalendar
15014 Roo.bootstrap = Roo.bootstrap || {};
15016 * @class Roo.bootstrap.Calendar
15017 * @extends Roo.bootstrap.Component
15018 * Bootstrap Calendar class
15019 * @cfg {Boolean} loadMask (true|false) default false
15020 * @cfg {Object} header generate the user specific header of the calendar, default false
15023 * Create a new Container
15024 * @param {Object} config The config object
15029 Roo.bootstrap.Calendar = function(config){
15030 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15034 * Fires when a date is selected
15035 * @param {DatePicker} this
15036 * @param {Date} date The selected date
15040 * @event monthchange
15041 * Fires when the displayed month changes
15042 * @param {DatePicker} this
15043 * @param {Date} date The selected month
15045 'monthchange': true,
15047 * @event evententer
15048 * Fires when mouse over an event
15049 * @param {Calendar} this
15050 * @param {event} Event
15052 'evententer': true,
15054 * @event eventleave
15055 * Fires when the mouse leaves an
15056 * @param {Calendar} this
15059 'eventleave': true,
15061 * @event eventclick
15062 * Fires when the mouse click an
15063 * @param {Calendar} this
15072 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15075 * @cfg {Number} startDay
15076 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15084 getAutoCreate : function(){
15087 var fc_button = function(name, corner, style, content ) {
15088 return Roo.apply({},{
15090 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15092 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15095 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15106 style : 'width:100%',
15113 cls : 'fc-header-left',
15115 fc_button('prev', 'left', 'arrow', '‹' ),
15116 fc_button('next', 'right', 'arrow', '›' ),
15117 { tag: 'span', cls: 'fc-header-space' },
15118 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15126 cls : 'fc-header-center',
15130 cls: 'fc-header-title',
15133 html : 'month / year'
15141 cls : 'fc-header-right',
15143 /* fc_button('month', 'left', '', 'month' ),
15144 fc_button('week', '', '', 'week' ),
15145 fc_button('day', 'right', '', 'day' )
15157 header = this.header;
15160 var cal_heads = function() {
15162 // fixme - handle this.
15164 for (var i =0; i < Date.dayNames.length; i++) {
15165 var d = Date.dayNames[i];
15168 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15169 html : d.substring(0,3)
15173 ret[0].cls += ' fc-first';
15174 ret[6].cls += ' fc-last';
15177 var cal_cell = function(n) {
15180 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15185 cls: 'fc-day-number',
15189 cls: 'fc-day-content',
15193 style: 'position: relative;' // height: 17px;
15205 var cal_rows = function() {
15208 for (var r = 0; r < 6; r++) {
15215 for (var i =0; i < Date.dayNames.length; i++) {
15216 var d = Date.dayNames[i];
15217 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15220 row.cn[0].cls+=' fc-first';
15221 row.cn[0].cn[0].style = 'min-height:90px';
15222 row.cn[6].cls+=' fc-last';
15226 ret[0].cls += ' fc-first';
15227 ret[4].cls += ' fc-prev-last';
15228 ret[5].cls += ' fc-last';
15235 cls: 'fc-border-separate',
15236 style : 'width:100%',
15244 cls : 'fc-first fc-last',
15262 cls : 'fc-content',
15263 style : "position: relative;",
15266 cls : 'fc-view fc-view-month fc-grid',
15267 style : 'position: relative',
15268 unselectable : 'on',
15271 cls : 'fc-event-container',
15272 style : 'position:absolute;z-index:8;top:0;left:0;'
15290 initEvents : function()
15293 throw "can not find store for calendar";
15299 style: "text-align:center",
15303 style: "background-color:white;width:50%;margin:250 auto",
15307 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15318 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15320 var size = this.el.select('.fc-content', true).first().getSize();
15321 this.maskEl.setSize(size.width, size.height);
15322 this.maskEl.enableDisplayMode("block");
15323 if(!this.loadMask){
15324 this.maskEl.hide();
15327 this.store = Roo.factory(this.store, Roo.data);
15328 this.store.on('load', this.onLoad, this);
15329 this.store.on('beforeload', this.onBeforeLoad, this);
15333 this.cells = this.el.select('.fc-day',true);
15334 //Roo.log(this.cells);
15335 this.textNodes = this.el.query('.fc-day-number');
15336 this.cells.addClassOnOver('fc-state-hover');
15338 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15339 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15340 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15341 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15343 this.on('monthchange', this.onMonthChange, this);
15345 this.update(new Date().clearTime());
15348 resize : function() {
15349 var sz = this.el.getSize();
15351 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15352 this.el.select('.fc-day-content div',true).setHeight(34);
15357 showPrevMonth : function(e){
15358 this.update(this.activeDate.add("mo", -1));
15360 showToday : function(e){
15361 this.update(new Date().clearTime());
15364 showNextMonth : function(e){
15365 this.update(this.activeDate.add("mo", 1));
15369 showPrevYear : function(){
15370 this.update(this.activeDate.add("y", -1));
15374 showNextYear : function(){
15375 this.update(this.activeDate.add("y", 1));
15380 update : function(date)
15382 var vd = this.activeDate;
15383 this.activeDate = date;
15384 // if(vd && this.el){
15385 // var t = date.getTime();
15386 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15387 // Roo.log('using add remove');
15389 // this.fireEvent('monthchange', this, date);
15391 // this.cells.removeClass("fc-state-highlight");
15392 // this.cells.each(function(c){
15393 // if(c.dateValue == t){
15394 // c.addClass("fc-state-highlight");
15395 // setTimeout(function(){
15396 // try{c.dom.firstChild.focus();}catch(e){}
15406 var days = date.getDaysInMonth();
15408 var firstOfMonth = date.getFirstDateOfMonth();
15409 var startingPos = firstOfMonth.getDay()-this.startDay;
15411 if(startingPos < this.startDay){
15415 var pm = date.add(Date.MONTH, -1);
15416 var prevStart = pm.getDaysInMonth()-startingPos;
15418 this.cells = this.el.select('.fc-day',true);
15419 this.textNodes = this.el.query('.fc-day-number');
15420 this.cells.addClassOnOver('fc-state-hover');
15422 var cells = this.cells.elements;
15423 var textEls = this.textNodes;
15425 Roo.each(cells, function(cell){
15426 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15429 days += startingPos;
15431 // convert everything to numbers so it's fast
15432 var day = 86400000;
15433 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15436 //Roo.log(prevStart);
15438 var today = new Date().clearTime().getTime();
15439 var sel = date.clearTime().getTime();
15440 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15441 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15442 var ddMatch = this.disabledDatesRE;
15443 var ddText = this.disabledDatesText;
15444 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15445 var ddaysText = this.disabledDaysText;
15446 var format = this.format;
15448 var setCellClass = function(cal, cell){
15452 //Roo.log('set Cell Class');
15454 var t = d.getTime();
15458 cell.dateValue = t;
15460 cell.className += " fc-today";
15461 cell.className += " fc-state-highlight";
15462 cell.title = cal.todayText;
15465 // disable highlight in other month..
15466 //cell.className += " fc-state-highlight";
15471 cell.className = " fc-state-disabled";
15472 cell.title = cal.minText;
15476 cell.className = " fc-state-disabled";
15477 cell.title = cal.maxText;
15481 if(ddays.indexOf(d.getDay()) != -1){
15482 cell.title = ddaysText;
15483 cell.className = " fc-state-disabled";
15486 if(ddMatch && format){
15487 var fvalue = d.dateFormat(format);
15488 if(ddMatch.test(fvalue)){
15489 cell.title = ddText.replace("%0", fvalue);
15490 cell.className = " fc-state-disabled";
15494 if (!cell.initialClassName) {
15495 cell.initialClassName = cell.dom.className;
15498 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15503 for(; i < startingPos; i++) {
15504 textEls[i].innerHTML = (++prevStart);
15505 d.setDate(d.getDate()+1);
15507 cells[i].className = "fc-past fc-other-month";
15508 setCellClass(this, cells[i]);
15513 for(; i < days; i++){
15514 intDay = i - startingPos + 1;
15515 textEls[i].innerHTML = (intDay);
15516 d.setDate(d.getDate()+1);
15518 cells[i].className = ''; // "x-date-active";
15519 setCellClass(this, cells[i]);
15523 for(; i < 42; i++) {
15524 textEls[i].innerHTML = (++extraDays);
15525 d.setDate(d.getDate()+1);
15527 cells[i].className = "fc-future fc-other-month";
15528 setCellClass(this, cells[i]);
15531 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15533 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15535 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15536 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15538 if(totalRows != 6){
15539 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15540 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15543 this.fireEvent('monthchange', this, date);
15547 if(!this.internalRender){
15548 var main = this.el.dom.firstChild;
15549 var w = main.offsetWidth;
15550 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15551 Roo.fly(main).setWidth(w);
15552 this.internalRender = true;
15553 // opera does not respect the auto grow header center column
15554 // then, after it gets a width opera refuses to recalculate
15555 // without a second pass
15556 if(Roo.isOpera && !this.secondPass){
15557 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15558 this.secondPass = true;
15559 this.update.defer(10, this, [date]);
15566 findCell : function(dt) {
15567 dt = dt.clearTime().getTime();
15569 this.cells.each(function(c){
15570 //Roo.log("check " +c.dateValue + '?=' + dt);
15571 if(c.dateValue == dt){
15581 findCells : function(ev) {
15582 var s = ev.start.clone().clearTime().getTime();
15584 var e= ev.end.clone().clearTime().getTime();
15587 this.cells.each(function(c){
15588 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15590 if(c.dateValue > e){
15593 if(c.dateValue < s){
15602 // findBestRow: function(cells)
15606 // for (var i =0 ; i < cells.length;i++) {
15607 // ret = Math.max(cells[i].rows || 0,ret);
15614 addItem : function(ev)
15616 // look for vertical location slot in
15617 var cells = this.findCells(ev);
15619 // ev.row = this.findBestRow(cells);
15621 // work out the location.
15625 for(var i =0; i < cells.length; i++) {
15627 cells[i].row = cells[0].row;
15630 cells[i].row = cells[i].row + 1;
15640 if (crow.start.getY() == cells[i].getY()) {
15642 crow.end = cells[i];
15659 cells[0].events.push(ev);
15661 this.calevents.push(ev);
15664 clearEvents: function() {
15666 if(!this.calevents){
15670 Roo.each(this.cells.elements, function(c){
15676 Roo.each(this.calevents, function(e) {
15677 Roo.each(e.els, function(el) {
15678 el.un('mouseenter' ,this.onEventEnter, this);
15679 el.un('mouseleave' ,this.onEventLeave, this);
15684 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15690 renderEvents: function()
15694 this.cells.each(function(c) {
15703 if(c.row != c.events.length){
15704 r = 4 - (4 - (c.row - c.events.length));
15707 c.events = ev.slice(0, r);
15708 c.more = ev.slice(r);
15710 if(c.more.length && c.more.length == 1){
15711 c.events.push(c.more.pop());
15714 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15718 this.cells.each(function(c) {
15720 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15723 for (var e = 0; e < c.events.length; e++){
15724 var ev = c.events[e];
15725 var rows = ev.rows;
15727 for(var i = 0; i < rows.length; i++) {
15729 // how many rows should it span..
15732 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15733 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15735 unselectable : "on",
15738 cls: 'fc-event-inner',
15742 // cls: 'fc-event-time',
15743 // html : cells.length > 1 ? '' : ev.time
15747 cls: 'fc-event-title',
15748 html : String.format('{0}', ev.title)
15755 cls: 'ui-resizable-handle ui-resizable-e',
15756 html : '  '
15763 cfg.cls += ' fc-event-start';
15765 if ((i+1) == rows.length) {
15766 cfg.cls += ' fc-event-end';
15769 var ctr = _this.el.select('.fc-event-container',true).first();
15770 var cg = ctr.createChild(cfg);
15772 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15773 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15775 var r = (c.more.length) ? 1 : 0;
15776 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15777 cg.setWidth(ebox.right - sbox.x -2);
15779 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15780 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15781 cg.on('click', _this.onEventClick, _this, ev);
15792 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15793 style : 'position: absolute',
15794 unselectable : "on",
15797 cls: 'fc-event-inner',
15801 cls: 'fc-event-title',
15809 cls: 'ui-resizable-handle ui-resizable-e',
15810 html : '  '
15816 var ctr = _this.el.select('.fc-event-container',true).first();
15817 var cg = ctr.createChild(cfg);
15819 var sbox = c.select('.fc-day-content',true).first().getBox();
15820 var ebox = c.select('.fc-day-content',true).first().getBox();
15822 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15823 cg.setWidth(ebox.right - sbox.x -2);
15825 cg.on('click', _this.onMoreEventClick, _this, c.more);
15835 onEventEnter: function (e, el,event,d) {
15836 this.fireEvent('evententer', this, el, event);
15839 onEventLeave: function (e, el,event,d) {
15840 this.fireEvent('eventleave', this, el, event);
15843 onEventClick: function (e, el,event,d) {
15844 this.fireEvent('eventclick', this, el, event);
15847 onMonthChange: function () {
15851 onMoreEventClick: function(e, el, more)
15855 this.calpopover.placement = 'right';
15856 this.calpopover.setTitle('More');
15858 this.calpopover.setContent('');
15860 var ctr = this.calpopover.el.select('.popover-content', true).first();
15862 Roo.each(more, function(m){
15864 cls : 'fc-event-hori fc-event-draggable',
15867 var cg = ctr.createChild(cfg);
15869 cg.on('click', _this.onEventClick, _this, m);
15872 this.calpopover.show(el);
15877 onLoad: function ()
15879 this.calevents = [];
15882 if(this.store.getCount() > 0){
15883 this.store.data.each(function(d){
15886 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15887 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15888 time : d.data.start_time,
15889 title : d.data.title,
15890 description : d.data.description,
15891 venue : d.data.venue
15896 this.renderEvents();
15898 if(this.calevents.length && this.loadMask){
15899 this.maskEl.hide();
15903 onBeforeLoad: function()
15905 this.clearEvents();
15907 this.maskEl.show();
15921 * @class Roo.bootstrap.Popover
15922 * @extends Roo.bootstrap.Component
15923 * Bootstrap Popover class
15924 * @cfg {String} html contents of the popover (or false to use children..)
15925 * @cfg {String} title of popover (or false to hide)
15926 * @cfg {String} placement how it is placed
15927 * @cfg {String} trigger click || hover (or false to trigger manually)
15928 * @cfg {String} over what (parent or false to trigger manually.)
15929 * @cfg {Number} delay - delay before showing
15932 * Create a new Popover
15933 * @param {Object} config The config object
15936 Roo.bootstrap.Popover = function(config){
15937 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15943 * After the popover show
15945 * @param {Roo.bootstrap.Popover} this
15950 * After the popover hide
15952 * @param {Roo.bootstrap.Popover} this
15958 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
15960 title: 'Fill in a title',
15963 placement : 'right',
15964 trigger : 'hover', // hover
15970 can_build_overlaid : false,
15972 getChildContainer : function()
15974 return this.el.select('.popover-content',true).first();
15977 getAutoCreate : function(){
15980 cls : 'popover roo-dynamic',
15981 style: 'display:block',
15987 cls : 'popover-inner',
15991 cls: 'popover-title',
15995 cls : 'popover-content',
16006 setTitle: function(str)
16009 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16011 setContent: function(str)
16014 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16016 // as it get's added to the bottom of the page.
16017 onRender : function(ct, position)
16019 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16021 var cfg = Roo.apply({}, this.getAutoCreate());
16025 cfg.cls += ' ' + this.cls;
16028 cfg.style = this.style;
16030 //Roo.log("adding to ");
16031 this.el = Roo.get(document.body).createChild(cfg, position);
16032 // Roo.log(this.el);
16037 initEvents : function()
16039 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16040 this.el.enableDisplayMode('block');
16042 if (this.over === false) {
16045 if (this.triggers === false) {
16048 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16049 var triggers = this.trigger ? this.trigger.split(' ') : [];
16050 Roo.each(triggers, function(trigger) {
16052 if (trigger == 'click') {
16053 on_el.on('click', this.toggle, this);
16054 } else if (trigger != 'manual') {
16055 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16056 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16058 on_el.on(eventIn ,this.enter, this);
16059 on_el.on(eventOut, this.leave, this);
16070 toggle : function () {
16071 this.hoverState == 'in' ? this.leave() : this.enter();
16074 enter : function () {
16076 clearTimeout(this.timeout);
16078 this.hoverState = 'in';
16080 if (!this.delay || !this.delay.show) {
16085 this.timeout = setTimeout(function () {
16086 if (_t.hoverState == 'in') {
16089 }, this.delay.show)
16092 leave : function() {
16093 clearTimeout(this.timeout);
16095 this.hoverState = 'out';
16097 if (!this.delay || !this.delay.hide) {
16102 this.timeout = setTimeout(function () {
16103 if (_t.hoverState == 'out') {
16106 }, this.delay.hide)
16109 show : function (on_el)
16112 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16116 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16117 if (this.html !== false) {
16118 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16120 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16121 if (!this.title.length) {
16122 this.el.select('.popover-title',true).hide();
16125 var placement = typeof this.placement == 'function' ?
16126 this.placement.call(this, this.el, on_el) :
16129 var autoToken = /\s?auto?\s?/i;
16130 var autoPlace = autoToken.test(placement);
16132 placement = placement.replace(autoToken, '') || 'top';
16136 //this.el.setXY([0,0]);
16138 this.el.dom.style.display='block';
16139 this.el.addClass(placement);
16141 //this.el.appendTo(on_el);
16143 var p = this.getPosition();
16144 var box = this.el.getBox();
16149 var align = Roo.bootstrap.Popover.alignment[placement];
16150 this.el.alignTo(on_el, align[0],align[1]);
16151 //var arrow = this.el.select('.arrow',true).first();
16152 //arrow.set(align[2],
16154 this.el.addClass('in');
16157 if (this.el.hasClass('fade')) {
16161 this.hoverState = 'in';
16163 this.fireEvent('show', this);
16168 this.el.setXY([0,0]);
16169 this.el.removeClass('in');
16171 this.hoverState = null;
16173 this.fireEvent('hide', this);
16178 Roo.bootstrap.Popover.alignment = {
16179 'left' : ['r-l', [-10,0], 'right'],
16180 'right' : ['l-r', [10,0], 'left'],
16181 'bottom' : ['t-b', [0,10], 'top'],
16182 'top' : [ 'b-t', [0,-10], 'bottom']
16193 * @class Roo.bootstrap.Progress
16194 * @extends Roo.bootstrap.Component
16195 * Bootstrap Progress class
16196 * @cfg {Boolean} striped striped of the progress bar
16197 * @cfg {Boolean} active animated of the progress bar
16201 * Create a new Progress
16202 * @param {Object} config The config object
16205 Roo.bootstrap.Progress = function(config){
16206 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16209 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16214 getAutoCreate : function(){
16222 cfg.cls += ' progress-striped';
16226 cfg.cls += ' active';
16245 * @class Roo.bootstrap.ProgressBar
16246 * @extends Roo.bootstrap.Component
16247 * Bootstrap ProgressBar class
16248 * @cfg {Number} aria_valuenow aria-value now
16249 * @cfg {Number} aria_valuemin aria-value min
16250 * @cfg {Number} aria_valuemax aria-value max
16251 * @cfg {String} label label for the progress bar
16252 * @cfg {String} panel (success | info | warning | danger )
16253 * @cfg {String} role role of the progress bar
16254 * @cfg {String} sr_only text
16258 * Create a new ProgressBar
16259 * @param {Object} config The config object
16262 Roo.bootstrap.ProgressBar = function(config){
16263 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16266 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16270 aria_valuemax : 100,
16276 getAutoCreate : function()
16281 cls: 'progress-bar',
16282 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16294 cfg.role = this.role;
16297 if(this.aria_valuenow){
16298 cfg['aria-valuenow'] = this.aria_valuenow;
16301 if(this.aria_valuemin){
16302 cfg['aria-valuemin'] = this.aria_valuemin;
16305 if(this.aria_valuemax){
16306 cfg['aria-valuemax'] = this.aria_valuemax;
16309 if(this.label && !this.sr_only){
16310 cfg.html = this.label;
16314 cfg.cls += ' progress-bar-' + this.panel;
16320 update : function(aria_valuenow)
16322 this.aria_valuenow = aria_valuenow;
16324 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16339 * @class Roo.bootstrap.TabGroup
16340 * @extends Roo.bootstrap.Column
16341 * Bootstrap Column class
16342 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16343 * @cfg {Boolean} carousel true to make the group behave like a carousel
16344 * @cfg {Boolean} bullets show bullets for the panels
16345 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16346 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16347 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16350 * Create a new TabGroup
16351 * @param {Object} config The config object
16354 Roo.bootstrap.TabGroup = function(config){
16355 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16357 this.navId = Roo.id();
16360 Roo.bootstrap.TabGroup.register(this);
16364 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16367 transition : false,
16372 slideOnTouch : false,
16374 getAutoCreate : function()
16376 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16378 cfg.cls += ' tab-content';
16380 if (this.carousel) {
16381 cfg.cls += ' carousel slide';
16384 cls : 'carousel-inner'
16387 if(this.bullets && !Roo.isTouch){
16390 cls : 'carousel-bullets',
16394 if(this.bullets_cls){
16395 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16398 for (var i = 0; i < this.bullets; i++){
16400 cls : 'bullet bullet-' + i
16408 cfg.cn[0].cn = bullets;
16415 initEvents: function()
16417 if(Roo.isTouch && this.slideOnTouch){
16418 this.el.on("touchstart", this.onTouchStart, this);
16421 if(this.autoslide){
16424 this.slideFn = window.setInterval(function() {
16425 _this.showPanelNext();
16431 onTouchStart : function(e, el, o)
16433 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16437 this.showPanelNext();
16440 getChildContainer : function()
16442 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16446 * register a Navigation item
16447 * @param {Roo.bootstrap.NavItem} the navitem to add
16449 register : function(item)
16451 this.tabs.push( item);
16452 item.navId = this.navId; // not really needed..
16457 getActivePanel : function()
16460 Roo.each(this.tabs, function(t) {
16470 getPanelByName : function(n)
16473 Roo.each(this.tabs, function(t) {
16474 if (t.tabId == n) {
16482 indexOfPanel : function(p)
16485 Roo.each(this.tabs, function(t,i) {
16486 if (t.tabId == p.tabId) {
16495 * show a specific panel
16496 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16497 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16499 showPanel : function (pan)
16501 if(this.transition || typeof(pan) == 'undefined'){
16502 Roo.log("waiting for the transitionend");
16506 if (typeof(pan) == 'number') {
16507 pan = this.tabs[pan];
16510 if (typeof(pan) == 'string') {
16511 pan = this.getPanelByName(pan);
16514 var cur = this.getActivePanel();
16517 Roo.log('pan or acitve pan is undefined');
16521 if (pan.tabId == this.getActivePanel().tabId) {
16525 if (false === cur.fireEvent('beforedeactivate')) {
16529 if(this.bullets > 0 && !Roo.isTouch){
16530 this.setActiveBullet(this.indexOfPanel(pan));
16533 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16535 this.transition = true;
16536 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16537 var lr = dir == 'next' ? 'left' : 'right';
16538 pan.el.addClass(dir); // or prev
16539 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16540 cur.el.addClass(lr); // or right
16541 pan.el.addClass(lr);
16544 cur.el.on('transitionend', function() {
16545 Roo.log("trans end?");
16547 pan.el.removeClass([lr,dir]);
16548 pan.setActive(true);
16550 cur.el.removeClass([lr]);
16551 cur.setActive(false);
16553 _this.transition = false;
16555 }, this, { single: true } );
16560 cur.setActive(false);
16561 pan.setActive(true);
16566 showPanelNext : function()
16568 var i = this.indexOfPanel(this.getActivePanel());
16570 if (i >= this.tabs.length - 1 && !this.autoslide) {
16574 if (i >= this.tabs.length - 1 && this.autoslide) {
16578 this.showPanel(this.tabs[i+1]);
16581 showPanelPrev : function()
16583 var i = this.indexOfPanel(this.getActivePanel());
16585 if (i < 1 && !this.autoslide) {
16589 if (i < 1 && this.autoslide) {
16590 i = this.tabs.length;
16593 this.showPanel(this.tabs[i-1]);
16597 addBullet: function()
16599 if(!this.bullets || Roo.isTouch){
16602 var ctr = this.el.select('.carousel-bullets',true).first();
16603 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16604 var bullet = ctr.createChild({
16605 cls : 'bullet bullet-' + i
16606 },ctr.dom.lastChild);
16611 bullet.on('click', (function(e, el, o, ii, t){
16613 e.preventDefault();
16615 this.showPanel(ii);
16617 if(this.autoslide && this.slideFn){
16618 clearInterval(this.slideFn);
16619 this.slideFn = window.setInterval(function() {
16620 _this.showPanelNext();
16624 }).createDelegate(this, [i, bullet], true));
16629 setActiveBullet : function(i)
16635 Roo.each(this.el.select('.bullet', true).elements, function(el){
16636 el.removeClass('selected');
16639 var bullet = this.el.select('.bullet-' + i, true).first();
16645 bullet.addClass('selected');
16656 Roo.apply(Roo.bootstrap.TabGroup, {
16660 * register a Navigation Group
16661 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16663 register : function(navgrp)
16665 this.groups[navgrp.navId] = navgrp;
16669 * fetch a Navigation Group based on the navigation ID
16670 * if one does not exist , it will get created.
16671 * @param {string} the navgroup to add
16672 * @returns {Roo.bootstrap.NavGroup} the navgroup
16674 get: function(navId) {
16675 if (typeof(this.groups[navId]) == 'undefined') {
16676 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16678 return this.groups[navId] ;
16693 * @class Roo.bootstrap.TabPanel
16694 * @extends Roo.bootstrap.Component
16695 * Bootstrap TabPanel class
16696 * @cfg {Boolean} active panel active
16697 * @cfg {String} html panel content
16698 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16699 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16703 * Create a new TabPanel
16704 * @param {Object} config The config object
16707 Roo.bootstrap.TabPanel = function(config){
16708 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16712 * Fires when the active status changes
16713 * @param {Roo.bootstrap.TabPanel} this
16714 * @param {Boolean} state the new state
16719 * @event beforedeactivate
16720 * Fires before a tab is de-activated - can be used to do validation on a form.
16721 * @param {Roo.bootstrap.TabPanel} this
16722 * @return {Boolean} false if there is an error
16725 'beforedeactivate': true
16728 this.tabId = this.tabId || Roo.id();
16732 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16739 getAutoCreate : function(){
16742 // item is needed for carousel - not sure if it has any effect otherwise
16743 cls: 'tab-pane item',
16744 html: this.html || ''
16748 cfg.cls += ' active';
16752 cfg.tabId = this.tabId;
16759 initEvents: function()
16761 var p = this.parent();
16762 this.navId = this.navId || p.navId;
16764 if (typeof(this.navId) != 'undefined') {
16765 // not really needed.. but just in case.. parent should be a NavGroup.
16766 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16770 var i = tg.tabs.length - 1;
16772 if(this.active && tg.bullets > 0 && i < tg.bullets){
16773 tg.setActiveBullet(i);
16780 onRender : function(ct, position)
16782 // Roo.log("Call onRender: " + this.xtype);
16784 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16792 setActive: function(state)
16794 Roo.log("panel - set active " + this.tabId + "=" + state);
16796 this.active = state;
16798 this.el.removeClass('active');
16800 } else if (!this.el.hasClass('active')) {
16801 this.el.addClass('active');
16804 this.fireEvent('changed', this, state);
16821 * @class Roo.bootstrap.DateField
16822 * @extends Roo.bootstrap.Input
16823 * Bootstrap DateField class
16824 * @cfg {Number} weekStart default 0
16825 * @cfg {String} viewMode default empty, (months|years)
16826 * @cfg {String} minViewMode default empty, (months|years)
16827 * @cfg {Number} startDate default -Infinity
16828 * @cfg {Number} endDate default Infinity
16829 * @cfg {Boolean} todayHighlight default false
16830 * @cfg {Boolean} todayBtn default false
16831 * @cfg {Boolean} calendarWeeks default false
16832 * @cfg {Object} daysOfWeekDisabled default empty
16833 * @cfg {Boolean} singleMode default false (true | false)
16835 * @cfg {Boolean} keyboardNavigation default true
16836 * @cfg {String} language default en
16839 * Create a new DateField
16840 * @param {Object} config The config object
16843 Roo.bootstrap.DateField = function(config){
16844 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16848 * Fires when this field show.
16849 * @param {Roo.bootstrap.DateField} this
16850 * @param {Mixed} date The date value
16855 * Fires when this field hide.
16856 * @param {Roo.bootstrap.DateField} this
16857 * @param {Mixed} date The date value
16862 * Fires when select a date.
16863 * @param {Roo.bootstrap.DateField} this
16864 * @param {Mixed} date The date value
16868 * @event beforeselect
16869 * Fires when before select a date.
16870 * @param {Roo.bootstrap.DateField} this
16871 * @param {Mixed} date The date value
16873 beforeselect : true
16877 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
16880 * @cfg {String} format
16881 * The default date format string which can be overriden for localization support. The format must be
16882 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16886 * @cfg {String} altFormats
16887 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16888 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16890 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16898 todayHighlight : false,
16904 keyboardNavigation: true,
16906 calendarWeeks: false,
16908 startDate: -Infinity,
16912 daysOfWeekDisabled: [],
16916 singleMode : false,
16918 UTCDate: function()
16920 return new Date(Date.UTC.apply(Date, arguments));
16923 UTCToday: function()
16925 var today = new Date();
16926 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16929 getDate: function() {
16930 var d = this.getUTCDate();
16931 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16934 getUTCDate: function() {
16938 setDate: function(d) {
16939 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16942 setUTCDate: function(d) {
16944 this.setValue(this.formatDate(this.date));
16947 onRender: function(ct, position)
16950 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16952 this.language = this.language || 'en';
16953 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16954 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16956 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16957 this.format = this.format || 'm/d/y';
16958 this.isInline = false;
16959 this.isInput = true;
16960 this.component = this.el.select('.add-on', true).first() || false;
16961 this.component = (this.component && this.component.length === 0) ? false : this.component;
16962 this.hasInput = this.component && this.inputEL().length;
16964 if (typeof(this.minViewMode === 'string')) {
16965 switch (this.minViewMode) {
16967 this.minViewMode = 1;
16970 this.minViewMode = 2;
16973 this.minViewMode = 0;
16978 if (typeof(this.viewMode === 'string')) {
16979 switch (this.viewMode) {
16992 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16994 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16996 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16998 this.picker().on('mousedown', this.onMousedown, this);
16999 this.picker().on('click', this.onClick, this);
17001 this.picker().addClass('datepicker-dropdown');
17003 this.startViewMode = this.viewMode;
17005 if(this.singleMode){
17006 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17007 v.setVisibilityMode(Roo.Element.DISPLAY);
17011 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17012 v.setStyle('width', '189px');
17016 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17017 if(!this.calendarWeeks){
17022 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17023 v.attr('colspan', function(i, val){
17024 return parseInt(val) + 1;
17029 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17031 this.setStartDate(this.startDate);
17032 this.setEndDate(this.endDate);
17034 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17041 if(this.isInline) {
17046 picker : function()
17048 return this.pickerEl;
17049 // return this.el.select('.datepicker', true).first();
17052 fillDow: function()
17054 var dowCnt = this.weekStart;
17063 if(this.calendarWeeks){
17071 while (dowCnt < this.weekStart + 7) {
17075 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17079 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17082 fillMonths: function()
17085 var months = this.picker().select('>.datepicker-months td', true).first();
17087 months.dom.innerHTML = '';
17093 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17096 months.createChild(month);
17103 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;
17105 if (this.date < this.startDate) {
17106 this.viewDate = new Date(this.startDate);
17107 } else if (this.date > this.endDate) {
17108 this.viewDate = new Date(this.endDate);
17110 this.viewDate = new Date(this.date);
17118 var d = new Date(this.viewDate),
17119 year = d.getUTCFullYear(),
17120 month = d.getUTCMonth(),
17121 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17122 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17123 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17124 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17125 currentDate = this.date && this.date.valueOf(),
17126 today = this.UTCToday();
17128 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17130 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17132 // this.picker.select('>tfoot th.today').
17133 // .text(dates[this.language].today)
17134 // .toggle(this.todayBtn !== false);
17136 this.updateNavArrows();
17139 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17141 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17143 prevMonth.setUTCDate(day);
17145 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17147 var nextMonth = new Date(prevMonth);
17149 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17151 nextMonth = nextMonth.valueOf();
17153 var fillMonths = false;
17155 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17157 while(prevMonth.valueOf() < nextMonth) {
17160 if (prevMonth.getUTCDay() === this.weekStart) {
17162 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17170 if(this.calendarWeeks){
17171 // ISO 8601: First week contains first thursday.
17172 // ISO also states week starts on Monday, but we can be more abstract here.
17174 // Start of current week: based on weekstart/current date
17175 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17176 // Thursday of this week
17177 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17178 // First Thursday of year, year from thursday
17179 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17180 // Calendar week: ms between thursdays, div ms per day, div 7 days
17181 calWeek = (th - yth) / 864e5 / 7 + 1;
17183 fillMonths.cn.push({
17191 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17193 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17196 if (this.todayHighlight &&
17197 prevMonth.getUTCFullYear() == today.getFullYear() &&
17198 prevMonth.getUTCMonth() == today.getMonth() &&
17199 prevMonth.getUTCDate() == today.getDate()) {
17200 clsName += ' today';
17203 if (currentDate && prevMonth.valueOf() === currentDate) {
17204 clsName += ' active';
17207 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17208 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17209 clsName += ' disabled';
17212 fillMonths.cn.push({
17214 cls: 'day ' + clsName,
17215 html: prevMonth.getDate()
17218 prevMonth.setDate(prevMonth.getDate()+1);
17221 var currentYear = this.date && this.date.getUTCFullYear();
17222 var currentMonth = this.date && this.date.getUTCMonth();
17224 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17226 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17227 v.removeClass('active');
17229 if(currentYear === year && k === currentMonth){
17230 v.addClass('active');
17233 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17234 v.addClass('disabled');
17240 year = parseInt(year/10, 10) * 10;
17242 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17244 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17247 for (var i = -1; i < 11; i++) {
17248 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17250 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17258 showMode: function(dir)
17261 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17264 Roo.each(this.picker().select('>div',true).elements, function(v){
17265 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17268 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17273 if(this.isInline) {
17277 this.picker().removeClass(['bottom', 'top']);
17279 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17281 * place to the top of element!
17285 this.picker().addClass('top');
17286 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17291 this.picker().addClass('bottom');
17293 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17296 parseDate : function(value)
17298 if(!value || value instanceof Date){
17301 var v = Date.parseDate(value, this.format);
17302 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17303 v = Date.parseDate(value, 'Y-m-d');
17305 if(!v && this.altFormats){
17306 if(!this.altFormatsArray){
17307 this.altFormatsArray = this.altFormats.split("|");
17309 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17310 v = Date.parseDate(value, this.altFormatsArray[i]);
17316 formatDate : function(date, fmt)
17318 return (!date || !(date instanceof Date)) ?
17319 date : date.dateFormat(fmt || this.format);
17322 onFocus : function()
17324 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17328 onBlur : function()
17330 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17332 var d = this.inputEl().getValue();
17341 this.picker().show();
17345 this.fireEvent('show', this, this.date);
17350 if(this.isInline) {
17353 this.picker().hide();
17354 this.viewMode = this.startViewMode;
17357 this.fireEvent('hide', this, this.date);
17361 onMousedown: function(e)
17363 e.stopPropagation();
17364 e.preventDefault();
17369 Roo.bootstrap.DateField.superclass.keyup.call(this);
17373 setValue: function(v)
17375 if(this.fireEvent('beforeselect', this, v) !== false){
17376 var d = new Date(this.parseDate(v) ).clearTime();
17378 if(isNaN(d.getTime())){
17379 this.date = this.viewDate = '';
17380 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17384 v = this.formatDate(d);
17386 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17388 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17392 this.fireEvent('select', this, this.date);
17396 getValue: function()
17398 return this.formatDate(this.date);
17401 fireKey: function(e)
17403 if (!this.picker().isVisible()){
17404 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17410 var dateChanged = false,
17412 newDate, newViewDate;
17417 e.preventDefault();
17421 if (!this.keyboardNavigation) {
17424 dir = e.keyCode == 37 ? -1 : 1;
17427 newDate = this.moveYear(this.date, dir);
17428 newViewDate = this.moveYear(this.viewDate, dir);
17429 } else if (e.shiftKey){
17430 newDate = this.moveMonth(this.date, dir);
17431 newViewDate = this.moveMonth(this.viewDate, dir);
17433 newDate = new Date(this.date);
17434 newDate.setUTCDate(this.date.getUTCDate() + dir);
17435 newViewDate = new Date(this.viewDate);
17436 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17438 if (this.dateWithinRange(newDate)){
17439 this.date = newDate;
17440 this.viewDate = newViewDate;
17441 this.setValue(this.formatDate(this.date));
17443 e.preventDefault();
17444 dateChanged = true;
17449 if (!this.keyboardNavigation) {
17452 dir = e.keyCode == 38 ? -1 : 1;
17454 newDate = this.moveYear(this.date, dir);
17455 newViewDate = this.moveYear(this.viewDate, dir);
17456 } else if (e.shiftKey){
17457 newDate = this.moveMonth(this.date, dir);
17458 newViewDate = this.moveMonth(this.viewDate, dir);
17460 newDate = new Date(this.date);
17461 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17462 newViewDate = new Date(this.viewDate);
17463 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17465 if (this.dateWithinRange(newDate)){
17466 this.date = newDate;
17467 this.viewDate = newViewDate;
17468 this.setValue(this.formatDate(this.date));
17470 e.preventDefault();
17471 dateChanged = true;
17475 this.setValue(this.formatDate(this.date));
17477 e.preventDefault();
17480 this.setValue(this.formatDate(this.date));
17494 onClick: function(e)
17496 e.stopPropagation();
17497 e.preventDefault();
17499 var target = e.getTarget();
17501 if(target.nodeName.toLowerCase() === 'i'){
17502 target = Roo.get(target).dom.parentNode;
17505 var nodeName = target.nodeName;
17506 var className = target.className;
17507 var html = target.innerHTML;
17508 //Roo.log(nodeName);
17510 switch(nodeName.toLowerCase()) {
17512 switch(className) {
17518 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17519 switch(this.viewMode){
17521 this.viewDate = this.moveMonth(this.viewDate, dir);
17525 this.viewDate = this.moveYear(this.viewDate, dir);
17531 var date = new Date();
17532 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17534 this.setValue(this.formatDate(this.date));
17541 if (className.indexOf('disabled') < 0) {
17542 this.viewDate.setUTCDate(1);
17543 if (className.indexOf('month') > -1) {
17544 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17546 var year = parseInt(html, 10) || 0;
17547 this.viewDate.setUTCFullYear(year);
17551 if(this.singleMode){
17552 this.setValue(this.formatDate(this.viewDate));
17563 //Roo.log(className);
17564 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17565 var day = parseInt(html, 10) || 1;
17566 var year = this.viewDate.getUTCFullYear(),
17567 month = this.viewDate.getUTCMonth();
17569 if (className.indexOf('old') > -1) {
17576 } else if (className.indexOf('new') > -1) {
17584 //Roo.log([year,month,day]);
17585 this.date = this.UTCDate(year, month, day,0,0,0,0);
17586 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17588 //Roo.log(this.formatDate(this.date));
17589 this.setValue(this.formatDate(this.date));
17596 setStartDate: function(startDate)
17598 this.startDate = startDate || -Infinity;
17599 if (this.startDate !== -Infinity) {
17600 this.startDate = this.parseDate(this.startDate);
17603 this.updateNavArrows();
17606 setEndDate: function(endDate)
17608 this.endDate = endDate || Infinity;
17609 if (this.endDate !== Infinity) {
17610 this.endDate = this.parseDate(this.endDate);
17613 this.updateNavArrows();
17616 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17618 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17619 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17620 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17622 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17623 return parseInt(d, 10);
17626 this.updateNavArrows();
17629 updateNavArrows: function()
17631 if(this.singleMode){
17635 var d = new Date(this.viewDate),
17636 year = d.getUTCFullYear(),
17637 month = d.getUTCMonth();
17639 Roo.each(this.picker().select('.prev', true).elements, function(v){
17641 switch (this.viewMode) {
17644 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17650 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17657 Roo.each(this.picker().select('.next', true).elements, function(v){
17659 switch (this.viewMode) {
17662 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17668 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17676 moveMonth: function(date, dir)
17681 var new_date = new Date(date.valueOf()),
17682 day = new_date.getUTCDate(),
17683 month = new_date.getUTCMonth(),
17684 mag = Math.abs(dir),
17686 dir = dir > 0 ? 1 : -1;
17689 // If going back one month, make sure month is not current month
17690 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17692 return new_date.getUTCMonth() == month;
17694 // If going forward one month, make sure month is as expected
17695 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17697 return new_date.getUTCMonth() != new_month;
17699 new_month = month + dir;
17700 new_date.setUTCMonth(new_month);
17701 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17702 if (new_month < 0 || new_month > 11) {
17703 new_month = (new_month + 12) % 12;
17706 // For magnitudes >1, move one month at a time...
17707 for (var i=0; i<mag; i++) {
17708 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17709 new_date = this.moveMonth(new_date, dir);
17711 // ...then reset the day, keeping it in the new month
17712 new_month = new_date.getUTCMonth();
17713 new_date.setUTCDate(day);
17715 return new_month != new_date.getUTCMonth();
17718 // Common date-resetting loop -- if date is beyond end of month, make it
17721 new_date.setUTCDate(--day);
17722 new_date.setUTCMonth(new_month);
17727 moveYear: function(date, dir)
17729 return this.moveMonth(date, dir*12);
17732 dateWithinRange: function(date)
17734 return date >= this.startDate && date <= this.endDate;
17740 this.picker().remove();
17745 Roo.apply(Roo.bootstrap.DateField, {
17756 html: '<i class="fa fa-arrow-left"/>'
17766 html: '<i class="fa fa-arrow-right"/>'
17808 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17809 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17810 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17811 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17812 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17825 navFnc: 'FullYear',
17830 navFnc: 'FullYear',
17835 Roo.apply(Roo.bootstrap.DateField, {
17839 cls: 'datepicker dropdown-menu roo-dynamic',
17843 cls: 'datepicker-days',
17847 cls: 'table-condensed',
17849 Roo.bootstrap.DateField.head,
17853 Roo.bootstrap.DateField.footer
17860 cls: 'datepicker-months',
17864 cls: 'table-condensed',
17866 Roo.bootstrap.DateField.head,
17867 Roo.bootstrap.DateField.content,
17868 Roo.bootstrap.DateField.footer
17875 cls: 'datepicker-years',
17879 cls: 'table-condensed',
17881 Roo.bootstrap.DateField.head,
17882 Roo.bootstrap.DateField.content,
17883 Roo.bootstrap.DateField.footer
17902 * @class Roo.bootstrap.TimeField
17903 * @extends Roo.bootstrap.Input
17904 * Bootstrap DateField class
17908 * Create a new TimeField
17909 * @param {Object} config The config object
17912 Roo.bootstrap.TimeField = function(config){
17913 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17917 * Fires when this field show.
17918 * @param {Roo.bootstrap.DateField} thisthis
17919 * @param {Mixed} date The date value
17924 * Fires when this field hide.
17925 * @param {Roo.bootstrap.DateField} this
17926 * @param {Mixed} date The date value
17931 * Fires when select a date.
17932 * @param {Roo.bootstrap.DateField} this
17933 * @param {Mixed} date The date value
17939 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
17942 * @cfg {String} format
17943 * The default time format string which can be overriden for localization support. The format must be
17944 * valid according to {@link Date#parseDate} (defaults to 'H:i').
17948 onRender: function(ct, position)
17951 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17953 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17955 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17957 this.pop = this.picker().select('>.datepicker-time',true).first();
17958 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17960 this.picker().on('mousedown', this.onMousedown, this);
17961 this.picker().on('click', this.onClick, this);
17963 this.picker().addClass('datepicker-dropdown');
17968 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17969 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17970 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17971 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17972 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17973 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17977 fireKey: function(e){
17978 if (!this.picker().isVisible()){
17979 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17985 e.preventDefault();
17993 this.onTogglePeriod();
17996 this.onIncrementMinutes();
17999 this.onDecrementMinutes();
18008 onClick: function(e) {
18009 e.stopPropagation();
18010 e.preventDefault();
18013 picker : function()
18015 return this.el.select('.datepicker', true).first();
18018 fillTime: function()
18020 var time = this.pop.select('tbody', true).first();
18022 time.dom.innerHTML = '';
18037 cls: 'hours-up glyphicon glyphicon-chevron-up'
18057 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18078 cls: 'timepicker-hour',
18093 cls: 'timepicker-minute',
18108 cls: 'btn btn-primary period',
18130 cls: 'hours-down glyphicon glyphicon-chevron-down'
18150 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18168 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18175 var hours = this.time.getHours();
18176 var minutes = this.time.getMinutes();
18189 hours = hours - 12;
18193 hours = '0' + hours;
18197 minutes = '0' + minutes;
18200 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18201 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18202 this.pop.select('button', true).first().dom.innerHTML = period;
18208 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18210 var cls = ['bottom'];
18212 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18219 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18224 this.picker().addClass(cls.join('-'));
18228 Roo.each(cls, function(c){
18230 _this.picker().setTop(_this.inputEl().getHeight());
18234 _this.picker().setTop(0 - _this.picker().getHeight());
18239 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18243 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18250 onFocus : function()
18252 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18256 onBlur : function()
18258 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18264 this.picker().show();
18269 this.fireEvent('show', this, this.date);
18274 this.picker().hide();
18277 this.fireEvent('hide', this, this.date);
18280 setTime : function()
18283 this.setValue(this.time.format(this.format));
18285 this.fireEvent('select', this, this.date);
18290 onMousedown: function(e){
18291 e.stopPropagation();
18292 e.preventDefault();
18295 onIncrementHours: function()
18297 Roo.log('onIncrementHours');
18298 this.time = this.time.add(Date.HOUR, 1);
18303 onDecrementHours: function()
18305 Roo.log('onDecrementHours');
18306 this.time = this.time.add(Date.HOUR, -1);
18310 onIncrementMinutes: function()
18312 Roo.log('onIncrementMinutes');
18313 this.time = this.time.add(Date.MINUTE, 1);
18317 onDecrementMinutes: function()
18319 Roo.log('onDecrementMinutes');
18320 this.time = this.time.add(Date.MINUTE, -1);
18324 onTogglePeriod: function()
18326 Roo.log('onTogglePeriod');
18327 this.time = this.time.add(Date.HOUR, 12);
18334 Roo.apply(Roo.bootstrap.TimeField, {
18364 cls: 'btn btn-info ok',
18376 Roo.apply(Roo.bootstrap.TimeField, {
18380 cls: 'datepicker dropdown-menu',
18384 cls: 'datepicker-time',
18388 cls: 'table-condensed',
18390 Roo.bootstrap.TimeField.content,
18391 Roo.bootstrap.TimeField.footer
18410 * @class Roo.bootstrap.MonthField
18411 * @extends Roo.bootstrap.Input
18412 * Bootstrap MonthField class
18414 * @cfg {String} language default en
18417 * Create a new MonthField
18418 * @param {Object} config The config object
18421 Roo.bootstrap.MonthField = function(config){
18422 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18427 * Fires when this field show.
18428 * @param {Roo.bootstrap.MonthField} this
18429 * @param {Mixed} date The date value
18434 * Fires when this field hide.
18435 * @param {Roo.bootstrap.MonthField} this
18436 * @param {Mixed} date The date value
18441 * Fires when select a date.
18442 * @param {Roo.bootstrap.MonthField} this
18443 * @param {String} oldvalue The old value
18444 * @param {String} newvalue The new value
18450 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18452 onRender: function(ct, position)
18455 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18457 this.language = this.language || 'en';
18458 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18459 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18461 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18462 this.isInline = false;
18463 this.isInput = true;
18464 this.component = this.el.select('.add-on', true).first() || false;
18465 this.component = (this.component && this.component.length === 0) ? false : this.component;
18466 this.hasInput = this.component && this.inputEL().length;
18468 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18470 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18472 this.picker().on('mousedown', this.onMousedown, this);
18473 this.picker().on('click', this.onClick, this);
18475 this.picker().addClass('datepicker-dropdown');
18477 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18478 v.setStyle('width', '189px');
18485 if(this.isInline) {
18491 setValue: function(v, suppressEvent)
18493 var o = this.getValue();
18495 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18499 if(suppressEvent !== true){
18500 this.fireEvent('select', this, o, v);
18505 getValue: function()
18510 onClick: function(e)
18512 e.stopPropagation();
18513 e.preventDefault();
18515 var target = e.getTarget();
18517 if(target.nodeName.toLowerCase() === 'i'){
18518 target = Roo.get(target).dom.parentNode;
18521 var nodeName = target.nodeName;
18522 var className = target.className;
18523 var html = target.innerHTML;
18525 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18529 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18531 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18537 picker : function()
18539 return this.pickerEl;
18542 fillMonths: function()
18545 var months = this.picker().select('>.datepicker-months td', true).first();
18547 months.dom.innerHTML = '';
18553 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18556 months.createChild(month);
18565 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18566 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18569 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18570 e.removeClass('active');
18572 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18573 e.addClass('active');
18580 if(this.isInline) {
18584 this.picker().removeClass(['bottom', 'top']);
18586 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18588 * place to the top of element!
18592 this.picker().addClass('top');
18593 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18598 this.picker().addClass('bottom');
18600 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18603 onFocus : function()
18605 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18609 onBlur : function()
18611 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18613 var d = this.inputEl().getValue();
18622 this.picker().show();
18623 this.picker().select('>.datepicker-months', true).first().show();
18627 this.fireEvent('show', this, this.date);
18632 if(this.isInline) {
18635 this.picker().hide();
18636 this.fireEvent('hide', this, this.date);
18640 onMousedown: function(e)
18642 e.stopPropagation();
18643 e.preventDefault();
18648 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18652 fireKey: function(e)
18654 if (!this.picker().isVisible()){
18655 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18666 e.preventDefault();
18670 dir = e.keyCode == 37 ? -1 : 1;
18672 this.vIndex = this.vIndex + dir;
18674 if(this.vIndex < 0){
18678 if(this.vIndex > 11){
18682 if(isNaN(this.vIndex)){
18686 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18692 dir = e.keyCode == 38 ? -1 : 1;
18694 this.vIndex = this.vIndex + dir * 4;
18696 if(this.vIndex < 0){
18700 if(this.vIndex > 11){
18704 if(isNaN(this.vIndex)){
18708 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18713 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18714 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18718 e.preventDefault();
18721 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18722 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18738 this.picker().remove();
18743 Roo.apply(Roo.bootstrap.MonthField, {
18762 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18763 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18768 Roo.apply(Roo.bootstrap.MonthField, {
18772 cls: 'datepicker dropdown-menu roo-dynamic',
18776 cls: 'datepicker-months',
18780 cls: 'table-condensed',
18782 Roo.bootstrap.DateField.content
18802 * @class Roo.bootstrap.CheckBox
18803 * @extends Roo.bootstrap.Input
18804 * Bootstrap CheckBox class
18806 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18807 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18808 * @cfg {String} boxLabel The text that appears beside the checkbox
18809 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18810 * @cfg {Boolean} checked initnal the element
18811 * @cfg {Boolean} inline inline the element (default false)
18812 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18815 * Create a new CheckBox
18816 * @param {Object} config The config object
18819 Roo.bootstrap.CheckBox = function(config){
18820 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18825 * Fires when the element is checked or unchecked.
18826 * @param {Roo.bootstrap.CheckBox} this This input
18827 * @param {Boolean} checked The new checked value
18834 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18836 inputType: 'checkbox',
18844 getAutoCreate : function()
18846 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18852 cfg.cls = 'form-group ' + this.inputType; //input-group
18855 cfg.cls += ' ' + this.inputType + '-inline';
18861 type : this.inputType,
18862 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18863 cls : 'roo-' + this.inputType, //'form-box',
18864 placeholder : this.placeholder || ''
18868 if (this.weight) { // Validity check?
18869 cfg.cls += " " + this.inputType + "-" + this.weight;
18872 if (this.disabled) {
18873 input.disabled=true;
18877 input.checked = this.checked;
18881 input.name = this.name;
18885 input.cls += ' input-' + this.size;
18890 ['xs','sm','md','lg'].map(function(size){
18891 if (settings[size]) {
18892 cfg.cls += ' col-' + size + '-' + settings[size];
18896 var inputblock = input;
18898 if (this.before || this.after) {
18901 cls : 'input-group',
18906 inputblock.cn.push({
18908 cls : 'input-group-addon',
18913 inputblock.cn.push(input);
18916 inputblock.cn.push({
18918 cls : 'input-group-addon',
18925 if (align ==='left' && this.fieldLabel.length) {
18926 // Roo.log("left and has label");
18932 cls : 'control-label col-md-' + this.labelWidth,
18933 html : this.fieldLabel
18937 cls : "col-md-" + (12 - this.labelWidth),
18944 } else if ( this.fieldLabel.length) {
18945 // Roo.log(" label");
18949 tag: this.boxLabel ? 'span' : 'label',
18951 cls: 'control-label box-input-label',
18952 //cls : 'input-group-addon',
18953 html : this.fieldLabel
18963 // Roo.log(" no label && no align");
18964 cfg.cn = [ inputblock ] ;
18970 var boxLabelCfg = {
18972 //'for': id, // box label is handled by onclick - so no for...
18974 html: this.boxLabel
18978 boxLabelCfg.tooltip = this.tooltip;
18981 cfg.cn.push(boxLabelCfg);
18991 * return the real input element.
18993 inputEl: function ()
18995 return this.el.select('input.roo-' + this.inputType,true).first();
18998 labelEl: function()
19000 return this.el.select('label.control-label',true).first();
19002 /* depricated... */
19006 return this.labelEl();
19009 boxLabelEl: function()
19011 return this.el.select('label.box-label',true).first();
19014 initEvents : function()
19016 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19018 this.inputEl().on('click', this.onClick, this);
19020 if (this.boxLabel) {
19021 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19024 this.startValue = this.getValue();
19027 Roo.bootstrap.CheckBox.register(this);
19031 onClick : function()
19033 this.setChecked(!this.checked);
19036 setChecked : function(state,suppressEvent)
19038 this.startValue = this.getValue();
19040 if(this.inputType == 'radio'){
19042 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19043 e.dom.checked = false;
19046 this.inputEl().dom.checked = true;
19048 this.inputEl().dom.value = this.inputValue;
19050 if(suppressEvent !== true){
19051 this.fireEvent('check', this, true);
19059 this.checked = state;
19061 this.inputEl().dom.checked = state;
19063 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19065 if(suppressEvent !== true){
19066 this.fireEvent('check', this, state);
19072 getValue : function()
19074 if(this.inputType == 'radio'){
19075 return this.getGroupValue();
19078 return this.inputEl().getValue();
19082 getGroupValue : function()
19084 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19088 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19091 setValue : function(v,suppressEvent)
19093 if(this.inputType == 'radio'){
19094 this.setGroupValue(v, suppressEvent);
19098 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19103 setGroupValue : function(v, suppressEvent)
19105 this.startValue = this.getValue();
19107 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19108 e.dom.checked = false;
19110 if(e.dom.value == v){
19111 e.dom.checked = true;
19115 if(suppressEvent !== true){
19116 this.fireEvent('check', this, true);
19124 validate : function()
19128 (this.inputType == 'radio' && this.validateRadio()) ||
19129 (this.inputType == 'checkbox' && this.validateCheckbox())
19135 this.markInvalid();
19139 validateRadio : function()
19143 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19144 if(!e.dom.checked){
19156 validateCheckbox : function()
19159 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19162 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19170 for(var i in group){
19175 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19182 * Mark this field as valid
19184 markValid : function()
19186 if(this.allowBlank){
19192 this.fireEvent('valid', this);
19194 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19197 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19204 if(this.inputType == 'radio'){
19205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19206 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19207 e.findParent('.form-group', false, true).addClass(_this.validClass);
19214 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19215 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19219 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19225 for(var i in group){
19226 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19227 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19232 * Mark this field as invalid
19233 * @param {String} msg The validation message
19235 markInvalid : function(msg)
19237 if(this.allowBlank){
19243 this.fireEvent('invalid', this, msg);
19245 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19248 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19252 label.markInvalid();
19255 if(this.inputType == 'radio'){
19256 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19257 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19258 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19265 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19266 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19270 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19276 for(var i in group){
19277 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19278 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19285 Roo.apply(Roo.bootstrap.CheckBox, {
19290 * register a CheckBox Group
19291 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19293 register : function(checkbox)
19295 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19296 this.groups[checkbox.groupId] = {};
19299 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19303 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19307 * fetch a CheckBox Group based on the group ID
19308 * @param {string} the group ID
19309 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19311 get: function(groupId) {
19312 if (typeof(this.groups[groupId]) == 'undefined') {
19316 return this.groups[groupId] ;
19328 *<div class="radio">
19330 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19331 Option one is this and that—be sure to include why it's great
19338 *<label class="radio-inline">fieldLabel</label>
19339 *<label class="radio-inline">
19340 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19348 * @class Roo.bootstrap.Radio
19349 * @extends Roo.bootstrap.CheckBox
19350 * Bootstrap Radio class
19353 * Create a new Radio
19354 * @param {Object} config The config object
19357 Roo.bootstrap.Radio = function(config){
19358 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19362 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19364 inputType: 'radio',
19368 getAutoCreate : function()
19370 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19371 align = align || 'left'; // default...
19378 tag : this.inline ? 'span' : 'div',
19383 var inline = this.inline ? ' radio-inline' : '';
19387 // does not need for, as we wrap the input with it..
19389 cls : 'control-label box-label' + inline,
19392 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19396 //cls : 'control-label' + inline,
19397 html : this.fieldLabel,
19398 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19407 type : this.inputType,
19408 //value : (!this.checked) ? this.valueOff : this.inputValue,
19409 value : this.inputValue,
19411 placeholder : this.placeholder || '' // ?? needed????
19414 if (this.weight) { // Validity check?
19415 input.cls += " radio-" + this.weight;
19417 if (this.disabled) {
19418 input.disabled=true;
19422 input.checked = this.checked;
19426 input.name = this.name;
19430 input.cls += ' input-' + this.size;
19433 //?? can span's inline have a width??
19436 ['xs','sm','md','lg'].map(function(size){
19437 if (settings[size]) {
19438 cfg.cls += ' col-' + size + '-' + settings[size];
19442 var inputblock = input;
19444 if (this.before || this.after) {
19447 cls : 'input-group',
19452 inputblock.cn.push({
19454 cls : 'input-group-addon',
19458 inputblock.cn.push(input);
19460 inputblock.cn.push({
19462 cls : 'input-group-addon',
19470 if (this.fieldLabel && this.fieldLabel.length) {
19471 cfg.cn.push(fieldLabel);
19474 // normal bootstrap puts the input inside the label.
19475 // however with our styled version - it has to go after the input.
19477 //lbl.cn.push(inputblock);
19481 cls: 'radio' + inline,
19488 cfg.cn.push( lblwrap);
19493 html: this.boxLabel
19502 initEvents : function()
19504 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19506 this.inputEl().on('click', this.onClick, this);
19507 if (this.boxLabel) {
19508 //Roo.log('find label');
19509 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19514 inputEl: function ()
19516 return this.el.select('input.roo-radio',true).first();
19518 onClick : function()
19521 this.setChecked(true);
19524 setChecked : function(state,suppressEvent)
19527 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19528 v.dom.checked = false;
19531 Roo.log(this.inputEl().dom);
19532 this.checked = state;
19533 this.inputEl().dom.checked = state;
19535 if(suppressEvent !== true){
19536 this.fireEvent('check', this, state);
19539 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19543 getGroupValue : function()
19546 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19547 if(v.dom.checked == true){
19548 value = v.dom.value;
19556 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19557 * @return {Mixed} value The field value
19559 getValue : function(){
19560 return this.getGroupValue();
19566 //<script type="text/javascript">
19569 * Based Ext JS Library 1.1.1
19570 * Copyright(c) 2006-2007, Ext JS, LLC.
19576 * @class Roo.HtmlEditorCore
19577 * @extends Roo.Component
19578 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19580 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19583 Roo.HtmlEditorCore = function(config){
19586 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19591 * @event initialize
19592 * Fires when the editor is fully initialized (including the iframe)
19593 * @param {Roo.HtmlEditorCore} this
19598 * Fires when the editor is first receives the focus. Any insertion must wait
19599 * until after this event.
19600 * @param {Roo.HtmlEditorCore} this
19604 * @event beforesync
19605 * Fires before the textarea is updated with content from the editor iframe. Return false
19606 * to cancel the sync.
19607 * @param {Roo.HtmlEditorCore} this
19608 * @param {String} html
19612 * @event beforepush
19613 * Fires before the iframe editor is updated with content from the textarea. Return false
19614 * to cancel the push.
19615 * @param {Roo.HtmlEditorCore} this
19616 * @param {String} html
19621 * Fires when the textarea is updated with content from the editor iframe.
19622 * @param {Roo.HtmlEditorCore} this
19623 * @param {String} html
19628 * Fires when the iframe editor is updated with content from the textarea.
19629 * @param {Roo.HtmlEditorCore} this
19630 * @param {String} html
19635 * @event editorevent
19636 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19637 * @param {Roo.HtmlEditorCore} this
19643 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19645 // defaults : white / black...
19646 this.applyBlacklists();
19653 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19657 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19663 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19668 * @cfg {Number} height (in pixels)
19672 * @cfg {Number} width (in pixels)
19677 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19680 stylesheets: false,
19685 // private properties
19686 validationEvent : false,
19688 initialized : false,
19690 sourceEditMode : false,
19691 onFocus : Roo.emptyFn,
19693 hideMode:'offsets',
19697 // blacklist + whitelisted elements..
19704 * Protected method that will not generally be called directly. It
19705 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19706 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19708 getDocMarkup : function(){
19712 // inherit styels from page...??
19713 if (this.stylesheets === false) {
19715 Roo.get(document.head).select('style').each(function(node) {
19716 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19719 Roo.get(document.head).select('link').each(function(node) {
19720 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19723 } else if (!this.stylesheets.length) {
19725 st = '<style type="text/css">' +
19726 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19732 st += '<style type="text/css">' +
19733 'IMG { cursor: pointer } ' +
19737 return '<html><head>' + st +
19738 //<style type="text/css">' +
19739 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19741 ' </head><body class="roo-htmleditor-body"></body></html>';
19745 onRender : function(ct, position)
19748 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19749 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19752 this.el.dom.style.border = '0 none';
19753 this.el.dom.setAttribute('tabIndex', -1);
19754 this.el.addClass('x-hidden hide');
19758 if(Roo.isIE){ // fix IE 1px bogus margin
19759 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19763 this.frameId = Roo.id();
19767 var iframe = this.owner.wrap.createChild({
19769 cls: 'form-control', // bootstrap..
19771 name: this.frameId,
19772 frameBorder : 'no',
19773 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19778 this.iframe = iframe.dom;
19780 this.assignDocWin();
19782 this.doc.designMode = 'on';
19785 this.doc.write(this.getDocMarkup());
19789 var task = { // must defer to wait for browser to be ready
19791 //console.log("run task?" + this.doc.readyState);
19792 this.assignDocWin();
19793 if(this.doc.body || this.doc.readyState == 'complete'){
19795 this.doc.designMode="on";
19799 Roo.TaskMgr.stop(task);
19800 this.initEditor.defer(10, this);
19807 Roo.TaskMgr.start(task);
19812 onResize : function(w, h)
19814 Roo.log('resize: ' +w + ',' + h );
19815 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19819 if(typeof w == 'number'){
19821 this.iframe.style.width = w + 'px';
19823 if(typeof h == 'number'){
19825 this.iframe.style.height = h + 'px';
19827 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19834 * Toggles the editor between standard and source edit mode.
19835 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19837 toggleSourceEdit : function(sourceEditMode){
19839 this.sourceEditMode = sourceEditMode === true;
19841 if(this.sourceEditMode){
19843 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19846 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19847 //this.iframe.className = '';
19850 //this.setSize(this.owner.wrap.getSize());
19851 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19858 * Protected method that will not generally be called directly. If you need/want
19859 * custom HTML cleanup, this is the method you should override.
19860 * @param {String} html The HTML to be cleaned
19861 * return {String} The cleaned HTML
19863 cleanHtml : function(html){
19864 html = String(html);
19865 if(html.length > 5){
19866 if(Roo.isSafari){ // strip safari nonsense
19867 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19870 if(html == ' '){
19877 * HTML Editor -> Textarea
19878 * Protected method that will not generally be called directly. Syncs the contents
19879 * of the editor iframe with the textarea.
19881 syncValue : function(){
19882 if(this.initialized){
19883 var bd = (this.doc.body || this.doc.documentElement);
19884 //this.cleanUpPaste(); -- this is done else where and causes havoc..
19885 var html = bd.innerHTML;
19887 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19888 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19890 html = '<div style="'+m[0]+'">' + html + '</div>';
19893 html = this.cleanHtml(html);
19894 // fix up the special chars.. normaly like back quotes in word...
19895 // however we do not want to do this with chinese..
19896 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19897 var cc = b.charCodeAt();
19899 (cc >= 0x4E00 && cc < 0xA000 ) ||
19900 (cc >= 0x3400 && cc < 0x4E00 ) ||
19901 (cc >= 0xf900 && cc < 0xfb00 )
19907 if(this.owner.fireEvent('beforesync', this, html) !== false){
19908 this.el.dom.value = html;
19909 this.owner.fireEvent('sync', this, html);
19915 * Protected method that will not generally be called directly. Pushes the value of the textarea
19916 * into the iframe editor.
19918 pushValue : function(){
19919 if(this.initialized){
19920 var v = this.el.dom.value.trim();
19922 // if(v.length < 1){
19926 if(this.owner.fireEvent('beforepush', this, v) !== false){
19927 var d = (this.doc.body || this.doc.documentElement);
19929 this.cleanUpPaste();
19930 this.el.dom.value = d.innerHTML;
19931 this.owner.fireEvent('push', this, v);
19937 deferFocus : function(){
19938 this.focus.defer(10, this);
19942 focus : function(){
19943 if(this.win && !this.sourceEditMode){
19950 assignDocWin: function()
19952 var iframe = this.iframe;
19955 this.doc = iframe.contentWindow.document;
19956 this.win = iframe.contentWindow;
19958 // if (!Roo.get(this.frameId)) {
19961 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19962 // this.win = Roo.get(this.frameId).dom.contentWindow;
19964 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19968 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19969 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19974 initEditor : function(){
19975 //console.log("INIT EDITOR");
19976 this.assignDocWin();
19980 this.doc.designMode="on";
19982 this.doc.write(this.getDocMarkup());
19985 var dbody = (this.doc.body || this.doc.documentElement);
19986 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19987 // this copies styles from the containing element into thsi one..
19988 // not sure why we need all of this..
19989 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19991 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19992 //ss['background-attachment'] = 'fixed'; // w3c
19993 dbody.bgProperties = 'fixed'; // ie
19994 //Roo.DomHelper.applyStyles(dbody, ss);
19995 Roo.EventManager.on(this.doc, {
19996 //'mousedown': this.onEditorEvent,
19997 'mouseup': this.onEditorEvent,
19998 'dblclick': this.onEditorEvent,
19999 'click': this.onEditorEvent,
20000 'keyup': this.onEditorEvent,
20005 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20007 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20008 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20010 this.initialized = true;
20012 this.owner.fireEvent('initialize', this);
20017 onDestroy : function(){
20023 //for (var i =0; i < this.toolbars.length;i++) {
20024 // // fixme - ask toolbars for heights?
20025 // this.toolbars[i].onDestroy();
20028 //this.wrap.dom.innerHTML = '';
20029 //this.wrap.remove();
20034 onFirstFocus : function(){
20036 this.assignDocWin();
20039 this.activated = true;
20042 if(Roo.isGecko){ // prevent silly gecko errors
20044 var s = this.win.getSelection();
20045 if(!s.focusNode || s.focusNode.nodeType != 3){
20046 var r = s.getRangeAt(0);
20047 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20052 this.execCmd('useCSS', true);
20053 this.execCmd('styleWithCSS', false);
20056 this.owner.fireEvent('activate', this);
20060 adjustFont: function(btn){
20061 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20062 //if(Roo.isSafari){ // safari
20065 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20066 if(Roo.isSafari){ // safari
20067 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20068 v = (v < 10) ? 10 : v;
20069 v = (v > 48) ? 48 : v;
20070 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20075 v = Math.max(1, v+adjust);
20077 this.execCmd('FontSize', v );
20080 onEditorEvent : function(e)
20082 this.owner.fireEvent('editorevent', this, e);
20083 // this.updateToolbar();
20084 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20087 insertTag : function(tg)
20089 // could be a bit smarter... -> wrap the current selected tRoo..
20090 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20092 range = this.createRange(this.getSelection());
20093 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20094 wrappingNode.appendChild(range.extractContents());
20095 range.insertNode(wrappingNode);
20102 this.execCmd("formatblock", tg);
20106 insertText : function(txt)
20110 var range = this.createRange();
20111 range.deleteContents();
20112 //alert(Sender.getAttribute('label'));
20114 range.insertNode(this.doc.createTextNode(txt));
20120 * Executes a Midas editor command on the editor document and performs necessary focus and
20121 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20122 * @param {String} cmd The Midas command
20123 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20125 relayCmd : function(cmd, value){
20127 this.execCmd(cmd, value);
20128 this.owner.fireEvent('editorevent', this);
20129 //this.updateToolbar();
20130 this.owner.deferFocus();
20134 * Executes a Midas editor command directly on the editor document.
20135 * For visual commands, you should use {@link #relayCmd} instead.
20136 * <b>This should only be called after the editor is initialized.</b>
20137 * @param {String} cmd The Midas command
20138 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20140 execCmd : function(cmd, value){
20141 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20148 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20150 * @param {String} text | dom node..
20152 insertAtCursor : function(text)
20157 if(!this.activated){
20163 var r = this.doc.selection.createRange();
20174 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20178 // from jquery ui (MIT licenced)
20180 var win = this.win;
20182 if (win.getSelection && win.getSelection().getRangeAt) {
20183 range = win.getSelection().getRangeAt(0);
20184 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20185 range.insertNode(node);
20186 } else if (win.document.selection && win.document.selection.createRange) {
20187 // no firefox support
20188 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20189 win.document.selection.createRange().pasteHTML(txt);
20191 // no firefox support
20192 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20193 this.execCmd('InsertHTML', txt);
20202 mozKeyPress : function(e){
20204 var c = e.getCharCode(), cmd;
20207 c = String.fromCharCode(c).toLowerCase();
20221 this.cleanUpPaste.defer(100, this);
20229 e.preventDefault();
20237 fixKeys : function(){ // load time branching for fastest keydown performance
20239 return function(e){
20240 var k = e.getKey(), r;
20243 r = this.doc.selection.createRange();
20246 r.pasteHTML('    ');
20253 r = this.doc.selection.createRange();
20255 var target = r.parentElement();
20256 if(!target || target.tagName.toLowerCase() != 'li'){
20258 r.pasteHTML('<br />');
20264 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20265 this.cleanUpPaste.defer(100, this);
20271 }else if(Roo.isOpera){
20272 return function(e){
20273 var k = e.getKey();
20277 this.execCmd('InsertHTML','    ');
20280 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20281 this.cleanUpPaste.defer(100, this);
20286 }else if(Roo.isSafari){
20287 return function(e){
20288 var k = e.getKey();
20292 this.execCmd('InsertText','\t');
20296 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20297 this.cleanUpPaste.defer(100, this);
20305 getAllAncestors: function()
20307 var p = this.getSelectedNode();
20310 a.push(p); // push blank onto stack..
20311 p = this.getParentElement();
20315 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20319 a.push(this.doc.body);
20323 lastSelNode : false,
20326 getSelection : function()
20328 this.assignDocWin();
20329 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20332 getSelectedNode: function()
20334 // this may only work on Gecko!!!
20336 // should we cache this!!!!
20341 var range = this.createRange(this.getSelection()).cloneRange();
20344 var parent = range.parentElement();
20346 var testRange = range.duplicate();
20347 testRange.moveToElementText(parent);
20348 if (testRange.inRange(range)) {
20351 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20354 parent = parent.parentElement;
20359 // is ancestor a text element.
20360 var ac = range.commonAncestorContainer;
20361 if (ac.nodeType == 3) {
20362 ac = ac.parentNode;
20365 var ar = ac.childNodes;
20368 var other_nodes = [];
20369 var has_other_nodes = false;
20370 for (var i=0;i<ar.length;i++) {
20371 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20374 // fullly contained node.
20376 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20381 // probably selected..
20382 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20383 other_nodes.push(ar[i]);
20387 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20392 has_other_nodes = true;
20394 if (!nodes.length && other_nodes.length) {
20395 nodes= other_nodes;
20397 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20403 createRange: function(sel)
20405 // this has strange effects when using with
20406 // top toolbar - not sure if it's a great idea.
20407 //this.editor.contentWindow.focus();
20408 if (typeof sel != "undefined") {
20410 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20412 return this.doc.createRange();
20415 return this.doc.createRange();
20418 getParentElement: function()
20421 this.assignDocWin();
20422 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20424 var range = this.createRange(sel);
20427 var p = range.commonAncestorContainer;
20428 while (p.nodeType == 3) { // text node
20439 * Range intersection.. the hard stuff...
20443 * [ -- selected range --- ]
20447 * if end is before start or hits it. fail.
20448 * if start is after end or hits it fail.
20450 * if either hits (but other is outside. - then it's not
20456 // @see http://www.thismuchiknow.co.uk/?p=64.
20457 rangeIntersectsNode : function(range, node)
20459 var nodeRange = node.ownerDocument.createRange();
20461 nodeRange.selectNode(node);
20463 nodeRange.selectNodeContents(node);
20466 var rangeStartRange = range.cloneRange();
20467 rangeStartRange.collapse(true);
20469 var rangeEndRange = range.cloneRange();
20470 rangeEndRange.collapse(false);
20472 var nodeStartRange = nodeRange.cloneRange();
20473 nodeStartRange.collapse(true);
20475 var nodeEndRange = nodeRange.cloneRange();
20476 nodeEndRange.collapse(false);
20478 return rangeStartRange.compareBoundaryPoints(
20479 Range.START_TO_START, nodeEndRange) == -1 &&
20480 rangeEndRange.compareBoundaryPoints(
20481 Range.START_TO_START, nodeStartRange) == 1;
20485 rangeCompareNode : function(range, node)
20487 var nodeRange = node.ownerDocument.createRange();
20489 nodeRange.selectNode(node);
20491 nodeRange.selectNodeContents(node);
20495 range.collapse(true);
20497 nodeRange.collapse(true);
20499 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20500 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20502 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20504 var nodeIsBefore = ss == 1;
20505 var nodeIsAfter = ee == -1;
20507 if (nodeIsBefore && nodeIsAfter) {
20510 if (!nodeIsBefore && nodeIsAfter) {
20511 return 1; //right trailed.
20514 if (nodeIsBefore && !nodeIsAfter) {
20515 return 2; // left trailed.
20521 // private? - in a new class?
20522 cleanUpPaste : function()
20524 // cleans up the whole document..
20525 Roo.log('cleanuppaste');
20527 this.cleanUpChildren(this.doc.body);
20528 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20529 if (clean != this.doc.body.innerHTML) {
20530 this.doc.body.innerHTML = clean;
20535 cleanWordChars : function(input) {// change the chars to hex code
20536 var he = Roo.HtmlEditorCore;
20538 var output = input;
20539 Roo.each(he.swapCodes, function(sw) {
20540 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20542 output = output.replace(swapper, sw[1]);
20549 cleanUpChildren : function (n)
20551 if (!n.childNodes.length) {
20554 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20555 this.cleanUpChild(n.childNodes[i]);
20562 cleanUpChild : function (node)
20565 //console.log(node);
20566 if (node.nodeName == "#text") {
20567 // clean up silly Windows -- stuff?
20570 if (node.nodeName == "#comment") {
20571 node.parentNode.removeChild(node);
20572 // clean up silly Windows -- stuff?
20575 var lcname = node.tagName.toLowerCase();
20576 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20577 // whitelist of tags..
20579 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20581 node.parentNode.removeChild(node);
20586 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20588 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20589 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20591 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20592 // remove_keep_children = true;
20595 if (remove_keep_children) {
20596 this.cleanUpChildren(node);
20597 // inserts everything just before this node...
20598 while (node.childNodes.length) {
20599 var cn = node.childNodes[0];
20600 node.removeChild(cn);
20601 node.parentNode.insertBefore(cn, node);
20603 node.parentNode.removeChild(node);
20607 if (!node.attributes || !node.attributes.length) {
20608 this.cleanUpChildren(node);
20612 function cleanAttr(n,v)
20615 if (v.match(/^\./) || v.match(/^\//)) {
20618 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20621 if (v.match(/^#/)) {
20624 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20625 node.removeAttribute(n);
20629 var cwhite = this.cwhite;
20630 var cblack = this.cblack;
20632 function cleanStyle(n,v)
20634 if (v.match(/expression/)) { //XSS?? should we even bother..
20635 node.removeAttribute(n);
20639 var parts = v.split(/;/);
20642 Roo.each(parts, function(p) {
20643 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20647 var l = p.split(':').shift().replace(/\s+/g,'');
20648 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20650 if ( cwhite.length && cblack.indexOf(l) > -1) {
20651 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20652 //node.removeAttribute(n);
20656 // only allow 'c whitelisted system attributes'
20657 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20658 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20659 //node.removeAttribute(n);
20669 if (clean.length) {
20670 node.setAttribute(n, clean.join(';'));
20672 node.removeAttribute(n);
20678 for (var i = node.attributes.length-1; i > -1 ; i--) {
20679 var a = node.attributes[i];
20682 if (a.name.toLowerCase().substr(0,2)=='on') {
20683 node.removeAttribute(a.name);
20686 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20687 node.removeAttribute(a.name);
20690 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20691 cleanAttr(a.name,a.value); // fixme..
20694 if (a.name == 'style') {
20695 cleanStyle(a.name,a.value);
20698 /// clean up MS crap..
20699 // tecnically this should be a list of valid class'es..
20702 if (a.name == 'class') {
20703 if (a.value.match(/^Mso/)) {
20704 node.className = '';
20707 if (a.value.match(/body/)) {
20708 node.className = '';
20719 this.cleanUpChildren(node);
20725 * Clean up MS wordisms...
20727 cleanWord : function(node)
20732 this.cleanWord(this.doc.body);
20735 if (node.nodeName == "#text") {
20736 // clean up silly Windows -- stuff?
20739 if (node.nodeName == "#comment") {
20740 node.parentNode.removeChild(node);
20741 // clean up silly Windows -- stuff?
20745 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20746 node.parentNode.removeChild(node);
20750 // remove - but keep children..
20751 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20752 while (node.childNodes.length) {
20753 var cn = node.childNodes[0];
20754 node.removeChild(cn);
20755 node.parentNode.insertBefore(cn, node);
20757 node.parentNode.removeChild(node);
20758 this.iterateChildren(node, this.cleanWord);
20762 if (node.className.length) {
20764 var cn = node.className.split(/\W+/);
20766 Roo.each(cn, function(cls) {
20767 if (cls.match(/Mso[a-zA-Z]+/)) {
20772 node.className = cna.length ? cna.join(' ') : '';
20774 node.removeAttribute("class");
20778 if (node.hasAttribute("lang")) {
20779 node.removeAttribute("lang");
20782 if (node.hasAttribute("style")) {
20784 var styles = node.getAttribute("style").split(";");
20786 Roo.each(styles, function(s) {
20787 if (!s.match(/:/)) {
20790 var kv = s.split(":");
20791 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20794 // what ever is left... we allow.
20797 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20798 if (!nstyle.length) {
20799 node.removeAttribute('style');
20802 this.iterateChildren(node, this.cleanWord);
20808 * iterateChildren of a Node, calling fn each time, using this as the scole..
20809 * @param {DomNode} node node to iterate children of.
20810 * @param {Function} fn method of this class to call on each item.
20812 iterateChildren : function(node, fn)
20814 if (!node.childNodes.length) {
20817 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20818 fn.call(this, node.childNodes[i])
20824 * cleanTableWidths.
20826 * Quite often pasting from word etc.. results in tables with column and widths.
20827 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20830 cleanTableWidths : function(node)
20835 this.cleanTableWidths(this.doc.body);
20840 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20843 Roo.log(node.tagName);
20844 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20845 this.iterateChildren(node, this.cleanTableWidths);
20848 if (node.hasAttribute('width')) {
20849 node.removeAttribute('width');
20853 if (node.hasAttribute("style")) {
20856 var styles = node.getAttribute("style").split(";");
20858 Roo.each(styles, function(s) {
20859 if (!s.match(/:/)) {
20862 var kv = s.split(":");
20863 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20866 // what ever is left... we allow.
20869 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20870 if (!nstyle.length) {
20871 node.removeAttribute('style');
20875 this.iterateChildren(node, this.cleanTableWidths);
20883 domToHTML : function(currentElement, depth, nopadtext) {
20885 depth = depth || 0;
20886 nopadtext = nopadtext || false;
20888 if (!currentElement) {
20889 return this.domToHTML(this.doc.body);
20892 //Roo.log(currentElement);
20894 var allText = false;
20895 var nodeName = currentElement.nodeName;
20896 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20898 if (nodeName == '#text') {
20900 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20905 if (nodeName != 'BODY') {
20908 // Prints the node tagName, such as <A>, <IMG>, etc
20911 for(i = 0; i < currentElement.attributes.length;i++) {
20913 var aname = currentElement.attributes.item(i).name;
20914 if (!currentElement.attributes.item(i).value.length) {
20917 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20920 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20929 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20932 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20937 // Traverse the tree
20939 var currentElementChild = currentElement.childNodes.item(i);
20940 var allText = true;
20941 var innerHTML = '';
20943 while (currentElementChild) {
20944 // Formatting code (indent the tree so it looks nice on the screen)
20945 var nopad = nopadtext;
20946 if (lastnode == 'SPAN') {
20950 if (currentElementChild.nodeName == '#text') {
20951 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20952 toadd = nopadtext ? toadd : toadd.trim();
20953 if (!nopad && toadd.length > 80) {
20954 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
20956 innerHTML += toadd;
20959 currentElementChild = currentElement.childNodes.item(i);
20965 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
20967 // Recursively traverse the tree structure of the child node
20968 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
20969 lastnode = currentElementChild.nodeName;
20971 currentElementChild=currentElement.childNodes.item(i);
20977 // The remaining code is mostly for formatting the tree
20978 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
20983 ret+= "</"+tagName+">";
20989 applyBlacklists : function()
20991 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
20992 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
20996 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20997 if (b.indexOf(tag) > -1) {
21000 this.white.push(tag);
21004 Roo.each(w, function(tag) {
21005 if (b.indexOf(tag) > -1) {
21008 if (this.white.indexOf(tag) > -1) {
21011 this.white.push(tag);
21016 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21017 if (w.indexOf(tag) > -1) {
21020 this.black.push(tag);
21024 Roo.each(b, function(tag) {
21025 if (w.indexOf(tag) > -1) {
21028 if (this.black.indexOf(tag) > -1) {
21031 this.black.push(tag);
21036 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21037 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21041 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21042 if (b.indexOf(tag) > -1) {
21045 this.cwhite.push(tag);
21049 Roo.each(w, function(tag) {
21050 if (b.indexOf(tag) > -1) {
21053 if (this.cwhite.indexOf(tag) > -1) {
21056 this.cwhite.push(tag);
21061 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21062 if (w.indexOf(tag) > -1) {
21065 this.cblack.push(tag);
21069 Roo.each(b, function(tag) {
21070 if (w.indexOf(tag) > -1) {
21073 if (this.cblack.indexOf(tag) > -1) {
21076 this.cblack.push(tag);
21081 setStylesheets : function(stylesheets)
21083 if(typeof(stylesheets) == 'string'){
21084 Roo.get(this.iframe.contentDocument.head).createChild({
21086 rel : 'stylesheet',
21095 Roo.each(stylesheets, function(s) {
21100 Roo.get(_this.iframe.contentDocument.head).createChild({
21102 rel : 'stylesheet',
21111 removeStylesheets : function()
21115 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21120 // hide stuff that is not compatible
21134 * @event specialkey
21138 * @cfg {String} fieldClass @hide
21141 * @cfg {String} focusClass @hide
21144 * @cfg {String} autoCreate @hide
21147 * @cfg {String} inputType @hide
21150 * @cfg {String} invalidClass @hide
21153 * @cfg {String} invalidText @hide
21156 * @cfg {String} msgFx @hide
21159 * @cfg {String} validateOnBlur @hide
21163 Roo.HtmlEditorCore.white = [
21164 'area', 'br', 'img', 'input', 'hr', 'wbr',
21166 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21167 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21168 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21169 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21170 'table', 'ul', 'xmp',
21172 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21175 'dir', 'menu', 'ol', 'ul', 'dl',
21181 Roo.HtmlEditorCore.black = [
21182 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21184 'base', 'basefont', 'bgsound', 'blink', 'body',
21185 'frame', 'frameset', 'head', 'html', 'ilayer',
21186 'iframe', 'layer', 'link', 'meta', 'object',
21187 'script', 'style' ,'title', 'xml' // clean later..
21189 Roo.HtmlEditorCore.clean = [
21190 'script', 'style', 'title', 'xml'
21192 Roo.HtmlEditorCore.remove = [
21197 Roo.HtmlEditorCore.ablack = [
21201 Roo.HtmlEditorCore.aclean = [
21202 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21206 Roo.HtmlEditorCore.pwhite= [
21207 'http', 'https', 'mailto'
21210 // white listed style attributes.
21211 Roo.HtmlEditorCore.cwhite= [
21212 // 'text-align', /// default is to allow most things..
21218 // black listed style attributes.
21219 Roo.HtmlEditorCore.cblack= [
21220 // 'font-size' -- this can be set by the project
21224 Roo.HtmlEditorCore.swapCodes =[
21243 * @class Roo.bootstrap.HtmlEditor
21244 * @extends Roo.bootstrap.TextArea
21245 * Bootstrap HtmlEditor class
21248 * Create a new HtmlEditor
21249 * @param {Object} config The config object
21252 Roo.bootstrap.HtmlEditor = function(config){
21253 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21254 if (!this.toolbars) {
21255 this.toolbars = [];
21257 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21260 * @event initialize
21261 * Fires when the editor is fully initialized (including the iframe)
21262 * @param {HtmlEditor} this
21267 * Fires when the editor is first receives the focus. Any insertion must wait
21268 * until after this event.
21269 * @param {HtmlEditor} this
21273 * @event beforesync
21274 * Fires before the textarea is updated with content from the editor iframe. Return false
21275 * to cancel the sync.
21276 * @param {HtmlEditor} this
21277 * @param {String} html
21281 * @event beforepush
21282 * Fires before the iframe editor is updated with content from the textarea. Return false
21283 * to cancel the push.
21284 * @param {HtmlEditor} this
21285 * @param {String} html
21290 * Fires when the textarea is updated with content from the editor iframe.
21291 * @param {HtmlEditor} this
21292 * @param {String} html
21297 * Fires when the iframe editor is updated with content from the textarea.
21298 * @param {HtmlEditor} this
21299 * @param {String} html
21303 * @event editmodechange
21304 * Fires when the editor switches edit modes
21305 * @param {HtmlEditor} this
21306 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21308 editmodechange: true,
21310 * @event editorevent
21311 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21312 * @param {HtmlEditor} this
21316 * @event firstfocus
21317 * Fires when on first focus - needed by toolbars..
21318 * @param {HtmlEditor} this
21323 * Auto save the htmlEditor value as a file into Events
21324 * @param {HtmlEditor} this
21328 * @event savedpreview
21329 * preview the saved version of htmlEditor
21330 * @param {HtmlEditor} this
21337 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21341 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21346 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21351 * @cfg {Number} height (in pixels)
21355 * @cfg {Number} width (in pixels)
21360 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21363 stylesheets: false,
21368 // private properties
21369 validationEvent : false,
21371 initialized : false,
21374 onFocus : Roo.emptyFn,
21376 hideMode:'offsets',
21379 tbContainer : false,
21381 toolbarContainer :function() {
21382 return this.wrap.select('.x-html-editor-tb',true).first();
21386 * Protected method that will not generally be called directly. It
21387 * is called when the editor creates its toolbar. Override this method if you need to
21388 * add custom toolbar buttons.
21389 * @param {HtmlEditor} editor
21391 createToolbar : function(){
21393 Roo.log("create toolbars");
21395 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21396 this.toolbars[0].render(this.toolbarContainer());
21400 // if (!editor.toolbars || !editor.toolbars.length) {
21401 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21404 // for (var i =0 ; i < editor.toolbars.length;i++) {
21405 // editor.toolbars[i] = Roo.factory(
21406 // typeof(editor.toolbars[i]) == 'string' ?
21407 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21408 // Roo.bootstrap.HtmlEditor);
21409 // editor.toolbars[i].init(editor);
21415 onRender : function(ct, position)
21417 // Roo.log("Call onRender: " + this.xtype);
21419 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21421 this.wrap = this.inputEl().wrap({
21422 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21425 this.editorcore.onRender(ct, position);
21427 if (this.resizable) {
21428 this.resizeEl = new Roo.Resizable(this.wrap, {
21432 minHeight : this.height,
21433 height: this.height,
21434 handles : this.resizable,
21437 resize : function(r, w, h) {
21438 _t.onResize(w,h); // -something
21444 this.createToolbar(this);
21447 if(!this.width && this.resizable){
21448 this.setSize(this.wrap.getSize());
21450 if (this.resizeEl) {
21451 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21452 // should trigger onReize..
21458 onResize : function(w, h)
21460 Roo.log('resize: ' +w + ',' + h );
21461 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21465 if(this.inputEl() ){
21466 if(typeof w == 'number'){
21467 var aw = w - this.wrap.getFrameWidth('lr');
21468 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21471 if(typeof h == 'number'){
21472 var tbh = -11; // fixme it needs to tool bar size!
21473 for (var i =0; i < this.toolbars.length;i++) {
21474 // fixme - ask toolbars for heights?
21475 tbh += this.toolbars[i].el.getHeight();
21476 //if (this.toolbars[i].footer) {
21477 // tbh += this.toolbars[i].footer.el.getHeight();
21485 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21486 ah -= 5; // knock a few pixes off for look..
21487 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21491 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21492 this.editorcore.onResize(ew,eh);
21497 * Toggles the editor between standard and source edit mode.
21498 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21500 toggleSourceEdit : function(sourceEditMode)
21502 this.editorcore.toggleSourceEdit(sourceEditMode);
21504 if(this.editorcore.sourceEditMode){
21505 Roo.log('editor - showing textarea');
21508 // Roo.log(this.syncValue());
21510 this.inputEl().removeClass(['hide', 'x-hidden']);
21511 this.inputEl().dom.removeAttribute('tabIndex');
21512 this.inputEl().focus();
21514 Roo.log('editor - hiding textarea');
21516 // Roo.log(this.pushValue());
21519 this.inputEl().addClass(['hide', 'x-hidden']);
21520 this.inputEl().dom.setAttribute('tabIndex', -1);
21521 //this.deferFocus();
21524 if(this.resizable){
21525 this.setSize(this.wrap.getSize());
21528 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21531 // private (for BoxComponent)
21532 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21534 // private (for BoxComponent)
21535 getResizeEl : function(){
21539 // private (for BoxComponent)
21540 getPositionEl : function(){
21545 initEvents : function(){
21546 this.originalValue = this.getValue();
21550 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21553 // markInvalid : Roo.emptyFn,
21555 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21558 // clearInvalid : Roo.emptyFn,
21560 setValue : function(v){
21561 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21562 this.editorcore.pushValue();
21567 deferFocus : function(){
21568 this.focus.defer(10, this);
21572 focus : function(){
21573 this.editorcore.focus();
21579 onDestroy : function(){
21585 for (var i =0; i < this.toolbars.length;i++) {
21586 // fixme - ask toolbars for heights?
21587 this.toolbars[i].onDestroy();
21590 this.wrap.dom.innerHTML = '';
21591 this.wrap.remove();
21596 onFirstFocus : function(){
21597 //Roo.log("onFirstFocus");
21598 this.editorcore.onFirstFocus();
21599 for (var i =0; i < this.toolbars.length;i++) {
21600 this.toolbars[i].onFirstFocus();
21606 syncValue : function()
21608 this.editorcore.syncValue();
21611 pushValue : function()
21613 this.editorcore.pushValue();
21617 // hide stuff that is not compatible
21631 * @event specialkey
21635 * @cfg {String} fieldClass @hide
21638 * @cfg {String} focusClass @hide
21641 * @cfg {String} autoCreate @hide
21644 * @cfg {String} inputType @hide
21647 * @cfg {String} invalidClass @hide
21650 * @cfg {String} invalidText @hide
21653 * @cfg {String} msgFx @hide
21656 * @cfg {String} validateOnBlur @hide
21665 Roo.namespace('Roo.bootstrap.htmleditor');
21667 * @class Roo.bootstrap.HtmlEditorToolbar1
21672 new Roo.bootstrap.HtmlEditor({
21675 new Roo.bootstrap.HtmlEditorToolbar1({
21676 disable : { fonts: 1 , format: 1, ..., ... , ...],
21682 * @cfg {Object} disable List of elements to disable..
21683 * @cfg {Array} btns List of additional buttons.
21687 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21690 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21693 Roo.apply(this, config);
21695 // default disabled, based on 'good practice'..
21696 this.disable = this.disable || {};
21697 Roo.applyIf(this.disable, {
21700 specialElements : true
21702 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21704 this.editor = config.editor;
21705 this.editorcore = config.editor.editorcore;
21707 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21709 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21710 // dont call parent... till later.
21712 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21717 editorcore : false,
21722 "h1","h2","h3","h4","h5","h6",
21724 "abbr", "acronym", "address", "cite", "samp", "var",
21728 onRender : function(ct, position)
21730 // Roo.log("Call onRender: " + this.xtype);
21732 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21734 this.el.dom.style.marginBottom = '0';
21736 var editorcore = this.editorcore;
21737 var editor= this.editor;
21740 var btn = function(id,cmd , toggle, handler){
21742 var event = toggle ? 'toggle' : 'click';
21747 xns: Roo.bootstrap,
21750 enableToggle:toggle !== false,
21752 pressed : toggle ? false : null,
21755 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21756 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21765 xns: Roo.bootstrap,
21766 glyphicon : 'font',
21770 xns: Roo.bootstrap,
21774 Roo.each(this.formats, function(f) {
21775 style.menu.items.push({
21777 xns: Roo.bootstrap,
21778 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21783 editorcore.insertTag(this.tagname);
21790 children.push(style);
21793 btn('bold',false,true);
21794 btn('italic',false,true);
21795 btn('align-left', 'justifyleft',true);
21796 btn('align-center', 'justifycenter',true);
21797 btn('align-right' , 'justifyright',true);
21798 btn('link', false, false, function(btn) {
21799 //Roo.log("create link?");
21800 var url = prompt(this.createLinkText, this.defaultLinkValue);
21801 if(url && url != 'http:/'+'/'){
21802 this.editorcore.relayCmd('createlink', url);
21805 btn('list','insertunorderedlist',true);
21806 btn('pencil', false,true, function(btn){
21809 this.toggleSourceEdit(btn.pressed);
21815 xns: Roo.bootstrap,
21820 xns: Roo.bootstrap,
21825 cog.menu.items.push({
21827 xns: Roo.bootstrap,
21828 html : Clean styles,
21833 editorcore.insertTag(this.tagname);
21842 this.xtype = 'NavSimplebar';
21844 for(var i=0;i< children.length;i++) {
21846 this.buttons.add(this.addxtypeChild(children[i]));
21850 editor.on('editorevent', this.updateToolbar, this);
21852 onBtnClick : function(id)
21854 this.editorcore.relayCmd(id);
21855 this.editorcore.focus();
21859 * Protected method that will not generally be called directly. It triggers
21860 * a toolbar update by reading the markup state of the current selection in the editor.
21862 updateToolbar: function(){
21864 if(!this.editorcore.activated){
21865 this.editor.onFirstFocus(); // is this neeed?
21869 var btns = this.buttons;
21870 var doc = this.editorcore.doc;
21871 btns.get('bold').setActive(doc.queryCommandState('bold'));
21872 btns.get('italic').setActive(doc.queryCommandState('italic'));
21873 //btns.get('underline').setActive(doc.queryCommandState('underline'));
21875 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21876 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21877 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21879 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21880 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21883 var ans = this.editorcore.getAllAncestors();
21884 if (this.formatCombo) {
21887 var store = this.formatCombo.store;
21888 this.formatCombo.setValue("");
21889 for (var i =0; i < ans.length;i++) {
21890 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21892 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21900 // hides menus... - so this cant be on a menu...
21901 Roo.bootstrap.MenuMgr.hideAll();
21903 Roo.bootstrap.MenuMgr.hideAll();
21904 //this.editorsyncValue();
21906 onFirstFocus: function() {
21907 this.buttons.each(function(item){
21911 toggleSourceEdit : function(sourceEditMode){
21914 if(sourceEditMode){
21915 Roo.log("disabling buttons");
21916 this.buttons.each( function(item){
21917 if(item.cmd != 'pencil'){
21923 Roo.log("enabling buttons");
21924 if(this.editorcore.initialized){
21925 this.buttons.each( function(item){
21931 Roo.log("calling toggole on editor");
21932 // tell the editor that it's been pressed..
21933 this.editor.toggleSourceEdit(sourceEditMode);
21943 * @class Roo.bootstrap.Table.AbstractSelectionModel
21944 * @extends Roo.util.Observable
21945 * Abstract base class for grid SelectionModels. It provides the interface that should be
21946 * implemented by descendant classes. This class should not be directly instantiated.
21949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21950 this.locked = false;
21951 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
21956 /** @ignore Called by the grid automatically. Do not call directly. */
21957 init : function(grid){
21963 * Locks the selections.
21966 this.locked = true;
21970 * Unlocks the selections.
21972 unlock : function(){
21973 this.locked = false;
21977 * Returns true if the selections are locked.
21978 * @return {Boolean}
21980 isLocked : function(){
21981 return this.locked;
21985 * @extends Roo.bootstrap.Table.AbstractSelectionModel
21986 * @class Roo.bootstrap.Table.RowSelectionModel
21987 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21988 * It supports multiple selections and keyboard selection/navigation.
21990 * @param {Object} config
21993 Roo.bootstrap.Table.RowSelectionModel = function(config){
21994 Roo.apply(this, config);
21995 this.selections = new Roo.util.MixedCollection(false, function(o){
22000 this.lastActive = false;
22004 * @event selectionchange
22005 * Fires when the selection changes
22006 * @param {SelectionModel} this
22008 "selectionchange" : true,
22010 * @event afterselectionchange
22011 * Fires after the selection changes (eg. by key press or clicking)
22012 * @param {SelectionModel} this
22014 "afterselectionchange" : true,
22016 * @event beforerowselect
22017 * Fires when a row is selected being selected, return false to cancel.
22018 * @param {SelectionModel} this
22019 * @param {Number} rowIndex The selected index
22020 * @param {Boolean} keepExisting False if other selections will be cleared
22022 "beforerowselect" : true,
22025 * Fires when a row is selected.
22026 * @param {SelectionModel} this
22027 * @param {Number} rowIndex The selected index
22028 * @param {Roo.data.Record} r The record
22030 "rowselect" : true,
22032 * @event rowdeselect
22033 * Fires when a row is deselected.
22034 * @param {SelectionModel} this
22035 * @param {Number} rowIndex The selected index
22037 "rowdeselect" : true
22039 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22040 this.locked = false;
22043 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22045 * @cfg {Boolean} singleSelect
22046 * True to allow selection of only one row at a time (defaults to false)
22048 singleSelect : false,
22051 initEvents : function(){
22053 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22054 this.grid.on("mousedown", this.handleMouseDown, this);
22055 }else{ // allow click to work like normal
22056 this.grid.on("rowclick", this.handleDragableRowClick, this);
22059 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22060 "up" : function(e){
22062 this.selectPrevious(e.shiftKey);
22063 }else if(this.last !== false && this.lastActive !== false){
22064 var last = this.last;
22065 this.selectRange(this.last, this.lastActive-1);
22066 this.grid.getView().focusRow(this.lastActive);
22067 if(last !== false){
22071 this.selectFirstRow();
22073 this.fireEvent("afterselectionchange", this);
22075 "down" : function(e){
22077 this.selectNext(e.shiftKey);
22078 }else if(this.last !== false && this.lastActive !== false){
22079 var last = this.last;
22080 this.selectRange(this.last, this.lastActive+1);
22081 this.grid.getView().focusRow(this.lastActive);
22082 if(last !== false){
22086 this.selectFirstRow();
22088 this.fireEvent("afterselectionchange", this);
22093 var view = this.grid.view;
22094 view.on("refresh", this.onRefresh, this);
22095 view.on("rowupdated", this.onRowUpdated, this);
22096 view.on("rowremoved", this.onRemove, this);
22100 onRefresh : function(){
22101 var ds = this.grid.dataSource, i, v = this.grid.view;
22102 var s = this.selections;
22103 s.each(function(r){
22104 if((i = ds.indexOfId(r.id)) != -1){
22113 onRemove : function(v, index, r){
22114 this.selections.remove(r);
22118 onRowUpdated : function(v, index, r){
22119 if(this.isSelected(r)){
22120 v.onRowSelect(index);
22126 * @param {Array} records The records to select
22127 * @param {Boolean} keepExisting (optional) True to keep existing selections
22129 selectRecords : function(records, keepExisting){
22131 this.clearSelections();
22133 var ds = this.grid.dataSource;
22134 for(var i = 0, len = records.length; i < len; i++){
22135 this.selectRow(ds.indexOf(records[i]), true);
22140 * Gets the number of selected rows.
22143 getCount : function(){
22144 return this.selections.length;
22148 * Selects the first row in the grid.
22150 selectFirstRow : function(){
22155 * Select the last row.
22156 * @param {Boolean} keepExisting (optional) True to keep existing selections
22158 selectLastRow : function(keepExisting){
22159 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22163 * Selects the row immediately following the last selected row.
22164 * @param {Boolean} keepExisting (optional) True to keep existing selections
22166 selectNext : function(keepExisting){
22167 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22168 this.selectRow(this.last+1, keepExisting);
22169 this.grid.getView().focusRow(this.last);
22174 * Selects the row that precedes the last selected row.
22175 * @param {Boolean} keepExisting (optional) True to keep existing selections
22177 selectPrevious : function(keepExisting){
22179 this.selectRow(this.last-1, keepExisting);
22180 this.grid.getView().focusRow(this.last);
22185 * Returns the selected records
22186 * @return {Array} Array of selected records
22188 getSelections : function(){
22189 return [].concat(this.selections.items);
22193 * Returns the first selected record.
22196 getSelected : function(){
22197 return this.selections.itemAt(0);
22202 * Clears all selections.
22204 clearSelections : function(fast){
22209 var ds = this.grid.dataSource;
22210 var s = this.selections;
22211 s.each(function(r){
22212 this.deselectRow(ds.indexOfId(r.id));
22216 this.selections.clear();
22223 * Selects all rows.
22225 selectAll : function(){
22229 this.selections.clear();
22230 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22231 this.selectRow(i, true);
22236 * Returns True if there is a selection.
22237 * @return {Boolean}
22239 hasSelection : function(){
22240 return this.selections.length > 0;
22244 * Returns True if the specified row is selected.
22245 * @param {Number/Record} record The record or index of the record to check
22246 * @return {Boolean}
22248 isSelected : function(index){
22249 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22250 return (r && this.selections.key(r.id) ? true : false);
22254 * Returns True if the specified record id is selected.
22255 * @param {String} id The id of record to check
22256 * @return {Boolean}
22258 isIdSelected : function(id){
22259 return (this.selections.key(id) ? true : false);
22263 handleMouseDown : function(e, t){
22264 var view = this.grid.getView(), rowIndex;
22265 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22268 if(e.shiftKey && this.last !== false){
22269 var last = this.last;
22270 this.selectRange(last, rowIndex, e.ctrlKey);
22271 this.last = last; // reset the last
22272 view.focusRow(rowIndex);
22274 var isSelected = this.isSelected(rowIndex);
22275 if(e.button !== 0 && isSelected){
22276 view.focusRow(rowIndex);
22277 }else if(e.ctrlKey && isSelected){
22278 this.deselectRow(rowIndex);
22279 }else if(!isSelected){
22280 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22281 view.focusRow(rowIndex);
22284 this.fireEvent("afterselectionchange", this);
22287 handleDragableRowClick : function(grid, rowIndex, e)
22289 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22290 this.selectRow(rowIndex, false);
22291 grid.view.focusRow(rowIndex);
22292 this.fireEvent("afterselectionchange", this);
22297 * Selects multiple rows.
22298 * @param {Array} rows Array of the indexes of the row to select
22299 * @param {Boolean} keepExisting (optional) True to keep existing selections
22301 selectRows : function(rows, keepExisting){
22303 this.clearSelections();
22305 for(var i = 0, len = rows.length; i < len; i++){
22306 this.selectRow(rows[i], true);
22311 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22312 * @param {Number} startRow The index of the first row in the range
22313 * @param {Number} endRow The index of the last row in the range
22314 * @param {Boolean} keepExisting (optional) True to retain existing selections
22316 selectRange : function(startRow, endRow, keepExisting){
22321 this.clearSelections();
22323 if(startRow <= endRow){
22324 for(var i = startRow; i <= endRow; i++){
22325 this.selectRow(i, true);
22328 for(var i = startRow; i >= endRow; i--){
22329 this.selectRow(i, true);
22335 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22336 * @param {Number} startRow The index of the first row in the range
22337 * @param {Number} endRow The index of the last row in the range
22339 deselectRange : function(startRow, endRow, preventViewNotify){
22343 for(var i = startRow; i <= endRow; i++){
22344 this.deselectRow(i, preventViewNotify);
22350 * @param {Number} row The index of the row to select
22351 * @param {Boolean} keepExisting (optional) True to keep existing selections
22353 selectRow : function(index, keepExisting, preventViewNotify){
22354 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22357 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22358 if(!keepExisting || this.singleSelect){
22359 this.clearSelections();
22361 var r = this.grid.dataSource.getAt(index);
22362 this.selections.add(r);
22363 this.last = this.lastActive = index;
22364 if(!preventViewNotify){
22365 this.grid.getView().onRowSelect(index);
22367 this.fireEvent("rowselect", this, index, r);
22368 this.fireEvent("selectionchange", this);
22374 * @param {Number} row The index of the row to deselect
22376 deselectRow : function(index, preventViewNotify){
22380 if(this.last == index){
22383 if(this.lastActive == index){
22384 this.lastActive = false;
22386 var r = this.grid.dataSource.getAt(index);
22387 this.selections.remove(r);
22388 if(!preventViewNotify){
22389 this.grid.getView().onRowDeselect(index);
22391 this.fireEvent("rowdeselect", this, index);
22392 this.fireEvent("selectionchange", this);
22396 restoreLast : function(){
22398 this.last = this._last;
22403 acceptsNav : function(row, col, cm){
22404 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22408 onEditorKey : function(field, e){
22409 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22414 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22416 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22418 }else if(k == e.ENTER && !e.ctrlKey){
22422 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22424 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22426 }else if(k == e.ESC){
22430 g.startEditing(newCell[0], newCell[1]);
22435 * Ext JS Library 1.1.1
22436 * Copyright(c) 2006-2007, Ext JS, LLC.
22438 * Originally Released Under LGPL - original licence link has changed is not relivant.
22441 * <script type="text/javascript">
22445 * @class Roo.bootstrap.PagingToolbar
22446 * @extends Roo.bootstrap.NavSimplebar
22447 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22449 * Create a new PagingToolbar
22450 * @param {Object} config The config object
22451 * @param {Roo.data.Store} store
22453 Roo.bootstrap.PagingToolbar = function(config)
22455 // old args format still supported... - xtype is prefered..
22456 // created from xtype...
22458 this.ds = config.dataSource;
22460 if (config.store && !this.ds) {
22461 this.store= Roo.factory(config.store, Roo.data);
22462 this.ds = this.store;
22463 this.ds.xmodule = this.xmodule || false;
22466 this.toolbarItems = [];
22467 if (config.items) {
22468 this.toolbarItems = config.items;
22471 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22476 this.bind(this.ds);
22479 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22483 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22485 * @cfg {Roo.data.Store} dataSource
22486 * The underlying data store providing the paged data
22489 * @cfg {String/HTMLElement/Element} container
22490 * container The id or element that will contain the toolbar
22493 * @cfg {Boolean} displayInfo
22494 * True to display the displayMsg (defaults to false)
22497 * @cfg {Number} pageSize
22498 * The number of records to display per page (defaults to 20)
22502 * @cfg {String} displayMsg
22503 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22505 displayMsg : 'Displaying {0} - {1} of {2}',
22507 * @cfg {String} emptyMsg
22508 * The message to display when no records are found (defaults to "No data to display")
22510 emptyMsg : 'No data to display',
22512 * Customizable piece of the default paging text (defaults to "Page")
22515 beforePageText : "Page",
22517 * Customizable piece of the default paging text (defaults to "of %0")
22520 afterPageText : "of {0}",
22522 * Customizable piece of the default paging text (defaults to "First Page")
22525 firstText : "First Page",
22527 * Customizable piece of the default paging text (defaults to "Previous Page")
22530 prevText : "Previous Page",
22532 * Customizable piece of the default paging text (defaults to "Next Page")
22535 nextText : "Next Page",
22537 * Customizable piece of the default paging text (defaults to "Last Page")
22540 lastText : "Last Page",
22542 * Customizable piece of the default paging text (defaults to "Refresh")
22545 refreshText : "Refresh",
22549 onRender : function(ct, position)
22551 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22552 this.navgroup.parentId = this.id;
22553 this.navgroup.onRender(this.el, null);
22554 // add the buttons to the navgroup
22556 if(this.displayInfo){
22557 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22558 this.displayEl = this.el.select('.x-paging-info', true).first();
22559 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22560 // this.displayEl = navel.el.select('span',true).first();
22566 Roo.each(_this.buttons, function(e){ // this might need to use render????
22567 Roo.factory(e).onRender(_this.el, null);
22571 Roo.each(_this.toolbarItems, function(e) {
22572 _this.navgroup.addItem(e);
22576 this.first = this.navgroup.addItem({
22577 tooltip: this.firstText,
22579 icon : 'fa fa-backward',
22581 preventDefault: true,
22582 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22585 this.prev = this.navgroup.addItem({
22586 tooltip: this.prevText,
22588 icon : 'fa fa-step-backward',
22590 preventDefault: true,
22591 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22593 //this.addSeparator();
22596 var field = this.navgroup.addItem( {
22598 cls : 'x-paging-position',
22600 html : this.beforePageText +
22601 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22602 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22605 this.field = field.el.select('input', true).first();
22606 this.field.on("keydown", this.onPagingKeydown, this);
22607 this.field.on("focus", function(){this.dom.select();});
22610 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22611 //this.field.setHeight(18);
22612 //this.addSeparator();
22613 this.next = this.navgroup.addItem({
22614 tooltip: this.nextText,
22616 html : ' <i class="fa fa-step-forward">',
22618 preventDefault: true,
22619 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22621 this.last = this.navgroup.addItem({
22622 tooltip: this.lastText,
22623 icon : 'fa fa-forward',
22626 preventDefault: true,
22627 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22629 //this.addSeparator();
22630 this.loading = this.navgroup.addItem({
22631 tooltip: this.refreshText,
22632 icon: 'fa fa-refresh',
22633 preventDefault: true,
22634 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22640 updateInfo : function(){
22641 if(this.displayEl){
22642 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22643 var msg = count == 0 ?
22647 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22649 this.displayEl.update(msg);
22654 onLoad : function(ds, r, o){
22655 this.cursor = o.params ? o.params.start : 0;
22656 var d = this.getPageData(),
22660 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22661 this.field.dom.value = ap;
22662 this.first.setDisabled(ap == 1);
22663 this.prev.setDisabled(ap == 1);
22664 this.next.setDisabled(ap == ps);
22665 this.last.setDisabled(ap == ps);
22666 this.loading.enable();
22671 getPageData : function(){
22672 var total = this.ds.getTotalCount();
22675 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22676 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22681 onLoadError : function(){
22682 this.loading.enable();
22686 onPagingKeydown : function(e){
22687 var k = e.getKey();
22688 var d = this.getPageData();
22690 var v = this.field.dom.value, pageNum;
22691 if(!v || isNaN(pageNum = parseInt(v, 10))){
22692 this.field.dom.value = d.activePage;
22695 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22696 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22699 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))
22701 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22702 this.field.dom.value = pageNum;
22703 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22706 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22708 var v = this.field.dom.value, pageNum;
22709 var increment = (e.shiftKey) ? 10 : 1;
22710 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22713 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22714 this.field.dom.value = d.activePage;
22717 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22719 this.field.dom.value = parseInt(v, 10) + increment;
22720 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22721 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22728 beforeLoad : function(){
22730 this.loading.disable();
22735 onClick : function(which){
22744 ds.load({params:{start: 0, limit: this.pageSize}});
22747 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22750 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22753 var total = ds.getTotalCount();
22754 var extra = total % this.pageSize;
22755 var lastStart = extra ? (total - extra) : total-this.pageSize;
22756 ds.load({params:{start: lastStart, limit: this.pageSize}});
22759 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22765 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22766 * @param {Roo.data.Store} store The data store to unbind
22768 unbind : function(ds){
22769 ds.un("beforeload", this.beforeLoad, this);
22770 ds.un("load", this.onLoad, this);
22771 ds.un("loadexception", this.onLoadError, this);
22772 ds.un("remove", this.updateInfo, this);
22773 ds.un("add", this.updateInfo, this);
22774 this.ds = undefined;
22778 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22779 * @param {Roo.data.Store} store The data store to bind
22781 bind : function(ds){
22782 ds.on("beforeload", this.beforeLoad, this);
22783 ds.on("load", this.onLoad, this);
22784 ds.on("loadexception", this.onLoadError, this);
22785 ds.on("remove", this.updateInfo, this);
22786 ds.on("add", this.updateInfo, this);
22797 * @class Roo.bootstrap.MessageBar
22798 * @extends Roo.bootstrap.Component
22799 * Bootstrap MessageBar class
22800 * @cfg {String} html contents of the MessageBar
22801 * @cfg {String} weight (info | success | warning | danger) default info
22802 * @cfg {String} beforeClass insert the bar before the given class
22803 * @cfg {Boolean} closable (true | false) default false
22804 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22807 * Create a new Element
22808 * @param {Object} config The config object
22811 Roo.bootstrap.MessageBar = function(config){
22812 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22815 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22821 beforeClass: 'bootstrap-sticky-wrap',
22823 getAutoCreate : function(){
22827 cls: 'alert alert-dismissable alert-' + this.weight,
22832 html: this.html || ''
22838 cfg.cls += ' alert-messages-fixed';
22852 onRender : function(ct, position)
22854 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22857 var cfg = Roo.apply({}, this.getAutoCreate());
22861 cfg.cls += ' ' + this.cls;
22864 cfg.style = this.style;
22866 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22868 this.el.setVisibilityMode(Roo.Element.DISPLAY);
22871 this.el.select('>button.close').on('click', this.hide, this);
22877 if (!this.rendered) {
22883 this.fireEvent('show', this);
22889 if (!this.rendered) {
22895 this.fireEvent('hide', this);
22898 update : function()
22900 // var e = this.el.dom.firstChild;
22902 // if(this.closable){
22903 // e = e.nextSibling;
22906 // e.data = this.html || '';
22908 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22924 * @class Roo.bootstrap.Graph
22925 * @extends Roo.bootstrap.Component
22926 * Bootstrap Graph class
22930 @cfg {String} graphtype bar | vbar | pie
22931 @cfg {number} g_x coodinator | centre x (pie)
22932 @cfg {number} g_y coodinator | centre y (pie)
22933 @cfg {number} g_r radius (pie)
22934 @cfg {number} g_height height of the chart (respected by all elements in the set)
22935 @cfg {number} g_width width of the chart (respected by all elements in the set)
22936 @cfg {Object} title The title of the chart
22939 -opts (object) options for the chart
22941 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22942 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22944 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.
22945 o stacked (boolean) whether or not to tread values as in a stacked bar chart
22947 o stretch (boolean)
22949 -opts (object) options for the pie
22952 o startAngle (number)
22953 o endAngle (number)
22957 * Create a new Input
22958 * @param {Object} config The config object
22961 Roo.bootstrap.Graph = function(config){
22962 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22968 * The img click event for the img.
22969 * @param {Roo.EventObject} e
22975 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
22986 //g_colors: this.colors,
22993 getAutoCreate : function(){
23004 onRender : function(ct,position){
23007 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23009 if (typeof(Raphael) == 'undefined') {
23010 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23014 this.raphael = Raphael(this.el.dom);
23016 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23017 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23018 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23019 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23021 r.text(160, 10, "Single Series Chart").attr(txtattr);
23022 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23023 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23024 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23026 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23027 r.barchart(330, 10, 300, 220, data1);
23028 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23029 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23032 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23033 // r.barchart(30, 30, 560, 250, xdata, {
23034 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23035 // axis : "0 0 1 1",
23036 // axisxlabels : xdata
23037 // //yvalues : cols,
23040 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23042 // this.load(null,xdata,{
23043 // axis : "0 0 1 1",
23044 // axisxlabels : xdata
23049 load : function(graphtype,xdata,opts)
23051 this.raphael.clear();
23053 graphtype = this.graphtype;
23058 var r = this.raphael,
23059 fin = function () {
23060 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23062 fout = function () {
23063 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23065 pfin = function() {
23066 this.sector.stop();
23067 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23070 this.label[0].stop();
23071 this.label[0].attr({ r: 7.5 });
23072 this.label[1].attr({ "font-weight": 800 });
23075 pfout = function() {
23076 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23079 this.label[0].animate({ r: 5 }, 500, "bounce");
23080 this.label[1].attr({ "font-weight": 400 });
23086 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23089 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23092 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23093 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23095 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23102 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23107 setTitle: function(o)
23112 initEvents: function() {
23115 this.el.on('click', this.onClick, this);
23119 onClick : function(e)
23121 Roo.log('img onclick');
23122 this.fireEvent('click', this, e);
23134 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23137 * @class Roo.bootstrap.dash.NumberBox
23138 * @extends Roo.bootstrap.Component
23139 * Bootstrap NumberBox class
23140 * @cfg {String} headline Box headline
23141 * @cfg {String} content Box content
23142 * @cfg {String} icon Box icon
23143 * @cfg {String} footer Footer text
23144 * @cfg {String} fhref Footer href
23147 * Create a new NumberBox
23148 * @param {Object} config The config object
23152 Roo.bootstrap.dash.NumberBox = function(config){
23153 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23157 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23166 getAutoCreate : function(){
23170 cls : 'small-box ',
23178 cls : 'roo-headline',
23179 html : this.headline
23183 cls : 'roo-content',
23184 html : this.content
23198 cls : 'ion ' + this.icon
23207 cls : 'small-box-footer',
23208 href : this.fhref || '#',
23212 cfg.cn.push(footer);
23219 onRender : function(ct,position){
23220 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23227 setHeadline: function (value)
23229 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23232 setFooter: function (value, href)
23234 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23237 this.el.select('a.small-box-footer',true).first().attr('href', href);
23242 setContent: function (value)
23244 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23247 initEvents: function()
23261 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23264 * @class Roo.bootstrap.dash.TabBox
23265 * @extends Roo.bootstrap.Component
23266 * Bootstrap TabBox class
23267 * @cfg {String} title Title of the TabBox
23268 * @cfg {String} icon Icon of the TabBox
23269 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23270 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23273 * Create a new TabBox
23274 * @param {Object} config The config object
23278 Roo.bootstrap.dash.TabBox = function(config){
23279 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23284 * When a pane is added
23285 * @param {Roo.bootstrap.dash.TabPane} pane
23289 * @event activatepane
23290 * When a pane is activated
23291 * @param {Roo.bootstrap.dash.TabPane} pane
23293 "activatepane" : true
23301 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23306 tabScrollable : false,
23308 getChildContainer : function()
23310 return this.el.select('.tab-content', true).first();
23313 getAutoCreate : function(){
23317 cls: 'pull-left header',
23325 cls: 'fa ' + this.icon
23331 cls: 'nav nav-tabs pull-right',
23337 if(this.tabScrollable){
23344 cls: 'nav nav-tabs pull-right',
23355 cls: 'nav-tabs-custom',
23360 cls: 'tab-content no-padding',
23368 initEvents : function()
23370 //Roo.log('add add pane handler');
23371 this.on('addpane', this.onAddPane, this);
23374 * Updates the box title
23375 * @param {String} html to set the title to.
23377 setTitle : function(value)
23379 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23381 onAddPane : function(pane)
23383 this.panes.push(pane);
23384 //Roo.log('addpane');
23386 // tabs are rendere left to right..
23387 if(!this.showtabs){
23391 var ctr = this.el.select('.nav-tabs', true).first();
23394 var existing = ctr.select('.nav-tab',true);
23395 var qty = existing.getCount();;
23398 var tab = ctr.createChild({
23400 cls : 'nav-tab' + (qty ? '' : ' active'),
23408 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23411 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23413 pane.el.addClass('active');
23418 onTabClick : function(ev,un,ob,pane)
23420 //Roo.log('tab - prev default');
23421 ev.preventDefault();
23424 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23425 pane.tab.addClass('active');
23426 //Roo.log(pane.title);
23427 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23428 // technically we should have a deactivate event.. but maybe add later.
23429 // and it should not de-activate the selected tab...
23430 this.fireEvent('activatepane', pane);
23431 pane.el.addClass('active');
23432 pane.fireEvent('activate');
23437 getActivePane : function()
23440 Roo.each(this.panes, function(p) {
23441 if(p.el.hasClass('active')){
23462 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23464 * @class Roo.bootstrap.TabPane
23465 * @extends Roo.bootstrap.Component
23466 * Bootstrap TabPane class
23467 * @cfg {Boolean} active (false | true) Default false
23468 * @cfg {String} title title of panel
23472 * Create a new TabPane
23473 * @param {Object} config The config object
23476 Roo.bootstrap.dash.TabPane = function(config){
23477 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23483 * When a pane is activated
23484 * @param {Roo.bootstrap.dash.TabPane} pane
23491 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23496 // the tabBox that this is attached to.
23499 getAutoCreate : function()
23507 cfg.cls += ' active';
23512 initEvents : function()
23514 //Roo.log('trigger add pane handler');
23515 this.parent().fireEvent('addpane', this)
23519 * Updates the tab title
23520 * @param {String} html to set the title to.
23522 setTitle: function(str)
23528 this.tab.select('a', true).first().dom.innerHTML = str;
23545 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23548 * @class Roo.bootstrap.menu.Menu
23549 * @extends Roo.bootstrap.Component
23550 * Bootstrap Menu class - container for Menu
23551 * @cfg {String} html Text of the menu
23552 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23553 * @cfg {String} icon Font awesome icon
23554 * @cfg {String} pos Menu align to (top | bottom) default bottom
23558 * Create a new Menu
23559 * @param {Object} config The config object
23563 Roo.bootstrap.menu.Menu = function(config){
23564 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23568 * @event beforeshow
23569 * Fires before this menu is displayed
23570 * @param {Roo.bootstrap.menu.Menu} this
23574 * @event beforehide
23575 * Fires before this menu is hidden
23576 * @param {Roo.bootstrap.menu.Menu} this
23581 * Fires after this menu is displayed
23582 * @param {Roo.bootstrap.menu.Menu} this
23587 * Fires after this menu is hidden
23588 * @param {Roo.bootstrap.menu.Menu} this
23593 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23594 * @param {Roo.bootstrap.menu.Menu} this
23595 * @param {Roo.EventObject} e
23602 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23606 weight : 'default',
23611 getChildContainer : function() {
23612 if(this.isSubMenu){
23616 return this.el.select('ul.dropdown-menu', true).first();
23619 getAutoCreate : function()
23624 cls : 'roo-menu-text',
23632 cls : 'fa ' + this.icon
23643 cls : 'dropdown-button btn btn-' + this.weight,
23648 cls : 'dropdown-toggle btn btn-' + this.weight,
23658 cls : 'dropdown-menu'
23664 if(this.pos == 'top'){
23665 cfg.cls += ' dropup';
23668 if(this.isSubMenu){
23671 cls : 'dropdown-menu'
23678 onRender : function(ct, position)
23680 this.isSubMenu = ct.hasClass('dropdown-submenu');
23682 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23685 initEvents : function()
23687 if(this.isSubMenu){
23691 this.hidden = true;
23693 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23694 this.triggerEl.on('click', this.onTriggerPress, this);
23696 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23697 this.buttonEl.on('click', this.onClick, this);
23703 if(this.isSubMenu){
23707 return this.el.select('ul.dropdown-menu', true).first();
23710 onClick : function(e)
23712 this.fireEvent("click", this, e);
23715 onTriggerPress : function(e)
23717 if (this.isVisible()) {
23724 isVisible : function(){
23725 return !this.hidden;
23730 this.fireEvent("beforeshow", this);
23732 this.hidden = false;
23733 this.el.addClass('open');
23735 Roo.get(document).on("mouseup", this.onMouseUp, this);
23737 this.fireEvent("show", this);
23744 this.fireEvent("beforehide", this);
23746 this.hidden = true;
23747 this.el.removeClass('open');
23749 Roo.get(document).un("mouseup", this.onMouseUp);
23751 this.fireEvent("hide", this);
23754 onMouseUp : function()
23768 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23771 * @class Roo.bootstrap.menu.Item
23772 * @extends Roo.bootstrap.Component
23773 * Bootstrap MenuItem class
23774 * @cfg {Boolean} submenu (true | false) default false
23775 * @cfg {String} html text of the item
23776 * @cfg {String} href the link
23777 * @cfg {Boolean} disable (true | false) default false
23778 * @cfg {Boolean} preventDefault (true | false) default true
23779 * @cfg {String} icon Font awesome icon
23780 * @cfg {String} pos Submenu align to (left | right) default right
23784 * Create a new Item
23785 * @param {Object} config The config object
23789 Roo.bootstrap.menu.Item = function(config){
23790 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23794 * Fires when the mouse is hovering over this menu
23795 * @param {Roo.bootstrap.menu.Item} this
23796 * @param {Roo.EventObject} e
23801 * Fires when the mouse exits this menu
23802 * @param {Roo.bootstrap.menu.Item} this
23803 * @param {Roo.EventObject} e
23809 * The raw click event for the entire grid.
23810 * @param {Roo.EventObject} e
23816 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23821 preventDefault: true,
23826 getAutoCreate : function()
23831 cls : 'roo-menu-item-text',
23839 cls : 'fa ' + this.icon
23848 href : this.href || '#',
23855 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23859 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23861 if(this.pos == 'left'){
23862 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23869 initEvents : function()
23871 this.el.on('mouseover', this.onMouseOver, this);
23872 this.el.on('mouseout', this.onMouseOut, this);
23874 this.el.select('a', true).first().on('click', this.onClick, this);
23878 onClick : function(e)
23880 if(this.preventDefault){
23881 e.preventDefault();
23884 this.fireEvent("click", this, e);
23887 onMouseOver : function(e)
23889 if(this.submenu && this.pos == 'left'){
23890 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23893 this.fireEvent("mouseover", this, e);
23896 onMouseOut : function(e)
23898 this.fireEvent("mouseout", this, e);
23910 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23913 * @class Roo.bootstrap.menu.Separator
23914 * @extends Roo.bootstrap.Component
23915 * Bootstrap Separator class
23918 * Create a new Separator
23919 * @param {Object} config The config object
23923 Roo.bootstrap.menu.Separator = function(config){
23924 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23927 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
23929 getAutoCreate : function(){
23950 * @class Roo.bootstrap.Tooltip
23951 * Bootstrap Tooltip class
23952 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23953 * to determine which dom element triggers the tooltip.
23955 * It needs to add support for additional attributes like tooltip-position
23958 * Create a new Toolti
23959 * @param {Object} config The config object
23962 Roo.bootstrap.Tooltip = function(config){
23963 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23966 Roo.apply(Roo.bootstrap.Tooltip, {
23968 * @function init initialize tooltip monitoring.
23972 currentTip : false,
23973 currentRegion : false,
23979 Roo.get(document).on('mouseover', this.enter ,this);
23980 Roo.get(document).on('mouseout', this.leave, this);
23983 this.currentTip = new Roo.bootstrap.Tooltip();
23986 enter : function(ev)
23988 var dom = ev.getTarget();
23990 //Roo.log(['enter',dom]);
23991 var el = Roo.fly(dom);
23992 if (this.currentEl) {
23994 //Roo.log(this.currentEl);
23995 //Roo.log(this.currentEl.contains(dom));
23996 if (this.currentEl == el) {
23999 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24005 if (this.currentTip.el) {
24006 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24011 // you can not look for children, as if el is the body.. then everythign is the child..
24012 if (!el.attr('tooltip')) { //
24013 if (!el.select("[tooltip]").elements.length) {
24016 // is the mouse over this child...?
24017 bindEl = el.select("[tooltip]").first();
24018 var xy = ev.getXY();
24019 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24020 //Roo.log("not in region.");
24023 //Roo.log("child element over..");
24026 this.currentEl = bindEl;
24027 this.currentTip.bind(bindEl);
24028 this.currentRegion = Roo.lib.Region.getRegion(dom);
24029 this.currentTip.enter();
24032 leave : function(ev)
24034 var dom = ev.getTarget();
24035 //Roo.log(['leave',dom]);
24036 if (!this.currentEl) {
24041 if (dom != this.currentEl.dom) {
24044 var xy = ev.getXY();
24045 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24048 // only activate leave if mouse cursor is outside... bounding box..
24053 if (this.currentTip) {
24054 this.currentTip.leave();
24056 //Roo.log('clear currentEl');
24057 this.currentEl = false;
24062 'left' : ['r-l', [-2,0], 'right'],
24063 'right' : ['l-r', [2,0], 'left'],
24064 'bottom' : ['t-b', [0,2], 'top'],
24065 'top' : [ 'b-t', [0,-2], 'bottom']
24071 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24076 delay : null, // can be { show : 300 , hide: 500}
24080 hoverState : null, //???
24082 placement : 'bottom',
24084 getAutoCreate : function(){
24091 cls : 'tooltip-arrow'
24094 cls : 'tooltip-inner'
24101 bind : function(el)
24107 enter : function () {
24109 if (this.timeout != null) {
24110 clearTimeout(this.timeout);
24113 this.hoverState = 'in';
24114 //Roo.log("enter - show");
24115 if (!this.delay || !this.delay.show) {
24120 this.timeout = setTimeout(function () {
24121 if (_t.hoverState == 'in') {
24124 }, this.delay.show);
24128 clearTimeout(this.timeout);
24130 this.hoverState = 'out';
24131 if (!this.delay || !this.delay.hide) {
24137 this.timeout = setTimeout(function () {
24138 //Roo.log("leave - timeout");
24140 if (_t.hoverState == 'out') {
24142 Roo.bootstrap.Tooltip.currentEl = false;
24150 this.render(document.body);
24153 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24155 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24157 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24159 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24161 var placement = typeof this.placement == 'function' ?
24162 this.placement.call(this, this.el, on_el) :
24165 var autoToken = /\s?auto?\s?/i;
24166 var autoPlace = autoToken.test(placement);
24168 placement = placement.replace(autoToken, '') || 'top';
24172 //this.el.setXY([0,0]);
24174 //this.el.dom.style.display='block';
24176 //this.el.appendTo(on_el);
24178 var p = this.getPosition();
24179 var box = this.el.getBox();
24185 var align = Roo.bootstrap.Tooltip.alignment[placement];
24187 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24189 if(placement == 'top' || placement == 'bottom'){
24191 placement = 'right';
24194 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24195 placement = 'left';
24199 align = Roo.bootstrap.Tooltip.alignment[placement];
24201 this.el.alignTo(this.bindEl, align[0],align[1]);
24202 //var arrow = this.el.select('.arrow',true).first();
24203 //arrow.set(align[2],
24205 this.el.addClass(placement);
24207 this.el.addClass('in fade');
24209 this.hoverState = null;
24211 if (this.el.hasClass('fade')) {
24222 //this.el.setXY([0,0]);
24223 this.el.removeClass('in');
24239 * @class Roo.bootstrap.LocationPicker
24240 * @extends Roo.bootstrap.Component
24241 * Bootstrap LocationPicker class
24242 * @cfg {Number} latitude Position when init default 0
24243 * @cfg {Number} longitude Position when init default 0
24244 * @cfg {Number} zoom default 15
24245 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24246 * @cfg {Boolean} mapTypeControl default false
24247 * @cfg {Boolean} disableDoubleClickZoom default false
24248 * @cfg {Boolean} scrollwheel default true
24249 * @cfg {Boolean} streetViewControl default false
24250 * @cfg {Number} radius default 0
24251 * @cfg {String} locationName
24252 * @cfg {Boolean} draggable default true
24253 * @cfg {Boolean} enableAutocomplete default false
24254 * @cfg {Boolean} enableReverseGeocode default true
24255 * @cfg {String} markerTitle
24258 * Create a new LocationPicker
24259 * @param {Object} config The config object
24263 Roo.bootstrap.LocationPicker = function(config){
24265 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24270 * Fires when the picker initialized.
24271 * @param {Roo.bootstrap.LocationPicker} this
24272 * @param {Google Location} location
24276 * @event positionchanged
24277 * Fires when the picker position changed.
24278 * @param {Roo.bootstrap.LocationPicker} this
24279 * @param {Google Location} location
24281 positionchanged : true,
24284 * Fires when the map resize.
24285 * @param {Roo.bootstrap.LocationPicker} this
24290 * Fires when the map show.
24291 * @param {Roo.bootstrap.LocationPicker} this
24296 * Fires when the map hide.
24297 * @param {Roo.bootstrap.LocationPicker} this
24302 * Fires when click the map.
24303 * @param {Roo.bootstrap.LocationPicker} this
24304 * @param {Map event} e
24308 * @event mapRightClick
24309 * Fires when right click the map.
24310 * @param {Roo.bootstrap.LocationPicker} this
24311 * @param {Map event} e
24313 mapRightClick : true,
24315 * @event markerClick
24316 * Fires when click the marker.
24317 * @param {Roo.bootstrap.LocationPicker} this
24318 * @param {Map event} e
24320 markerClick : true,
24322 * @event markerRightClick
24323 * Fires when right click the marker.
24324 * @param {Roo.bootstrap.LocationPicker} this
24325 * @param {Map event} e
24327 markerRightClick : true,
24329 * @event OverlayViewDraw
24330 * Fires when OverlayView Draw
24331 * @param {Roo.bootstrap.LocationPicker} this
24333 OverlayViewDraw : true,
24335 * @event OverlayViewOnAdd
24336 * Fires when OverlayView Draw
24337 * @param {Roo.bootstrap.LocationPicker} this
24339 OverlayViewOnAdd : true,
24341 * @event OverlayViewOnRemove
24342 * Fires when OverlayView Draw
24343 * @param {Roo.bootstrap.LocationPicker} this
24345 OverlayViewOnRemove : true,
24347 * @event OverlayViewShow
24348 * Fires when OverlayView Draw
24349 * @param {Roo.bootstrap.LocationPicker} this
24350 * @param {Pixel} cpx
24352 OverlayViewShow : true,
24354 * @event OverlayViewHide
24355 * Fires when OverlayView Draw
24356 * @param {Roo.bootstrap.LocationPicker} this
24358 OverlayViewHide : true,
24360 * @event loadexception
24361 * Fires when load google lib failed.
24362 * @param {Roo.bootstrap.LocationPicker} this
24364 loadexception : true
24369 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24371 gMapContext: false,
24377 mapTypeControl: false,
24378 disableDoubleClickZoom: false,
24380 streetViewControl: false,
24384 enableAutocomplete: false,
24385 enableReverseGeocode: true,
24388 getAutoCreate: function()
24393 cls: 'roo-location-picker'
24399 initEvents: function(ct, position)
24401 if(!this.el.getWidth() || this.isApplied()){
24405 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24410 initial: function()
24412 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24413 this.fireEvent('loadexception', this);
24417 if(!this.mapTypeId){
24418 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24421 this.gMapContext = this.GMapContext();
24423 this.initOverlayView();
24425 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24429 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24430 _this.setPosition(_this.gMapContext.marker.position);
24433 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24434 _this.fireEvent('mapClick', this, event);
24438 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24439 _this.fireEvent('mapRightClick', this, event);
24443 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24444 _this.fireEvent('markerClick', this, event);
24448 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24449 _this.fireEvent('markerRightClick', this, event);
24453 this.setPosition(this.gMapContext.location);
24455 this.fireEvent('initial', this, this.gMapContext.location);
24458 initOverlayView: function()
24462 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24466 _this.fireEvent('OverlayViewDraw', _this);
24471 _this.fireEvent('OverlayViewOnAdd', _this);
24474 onRemove: function()
24476 _this.fireEvent('OverlayViewOnRemove', _this);
24479 show: function(cpx)
24481 _this.fireEvent('OverlayViewShow', _this, cpx);
24486 _this.fireEvent('OverlayViewHide', _this);
24492 fromLatLngToContainerPixel: function(event)
24494 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24497 isApplied: function()
24499 return this.getGmapContext() == false ? false : true;
24502 getGmapContext: function()
24504 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24507 GMapContext: function()
24509 var position = new google.maps.LatLng(this.latitude, this.longitude);
24511 var _map = new google.maps.Map(this.el.dom, {
24514 mapTypeId: this.mapTypeId,
24515 mapTypeControl: this.mapTypeControl,
24516 disableDoubleClickZoom: this.disableDoubleClickZoom,
24517 scrollwheel: this.scrollwheel,
24518 streetViewControl: this.streetViewControl,
24519 locationName: this.locationName,
24520 draggable: this.draggable,
24521 enableAutocomplete: this.enableAutocomplete,
24522 enableReverseGeocode: this.enableReverseGeocode
24525 var _marker = new google.maps.Marker({
24526 position: position,
24528 title: this.markerTitle,
24529 draggable: this.draggable
24536 location: position,
24537 radius: this.radius,
24538 locationName: this.locationName,
24539 addressComponents: {
24540 formatted_address: null,
24541 addressLine1: null,
24542 addressLine2: null,
24544 streetNumber: null,
24548 stateOrProvince: null
24551 domContainer: this.el.dom,
24552 geodecoder: new google.maps.Geocoder()
24556 drawCircle: function(center, radius, options)
24558 if (this.gMapContext.circle != null) {
24559 this.gMapContext.circle.setMap(null);
24563 options = Roo.apply({}, options, {
24564 strokeColor: "#0000FF",
24565 strokeOpacity: .35,
24567 fillColor: "#0000FF",
24571 options.map = this.gMapContext.map;
24572 options.radius = radius;
24573 options.center = center;
24574 this.gMapContext.circle = new google.maps.Circle(options);
24575 return this.gMapContext.circle;
24581 setPosition: function(location)
24583 this.gMapContext.location = location;
24584 this.gMapContext.marker.setPosition(location);
24585 this.gMapContext.map.panTo(location);
24586 this.drawCircle(location, this.gMapContext.radius, {});
24590 if (this.gMapContext.settings.enableReverseGeocode) {
24591 this.gMapContext.geodecoder.geocode({
24592 latLng: this.gMapContext.location
24593 }, function(results, status) {
24595 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24596 _this.gMapContext.locationName = results[0].formatted_address;
24597 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24599 _this.fireEvent('positionchanged', this, location);
24606 this.fireEvent('positionchanged', this, location);
24611 google.maps.event.trigger(this.gMapContext.map, "resize");
24613 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24615 this.fireEvent('resize', this);
24618 setPositionByLatLng: function(latitude, longitude)
24620 this.setPosition(new google.maps.LatLng(latitude, longitude));
24623 getCurrentPosition: function()
24626 latitude: this.gMapContext.location.lat(),
24627 longitude: this.gMapContext.location.lng()
24631 getAddressName: function()
24633 return this.gMapContext.locationName;
24636 getAddressComponents: function()
24638 return this.gMapContext.addressComponents;
24641 address_component_from_google_geocode: function(address_components)
24645 for (var i = 0; i < address_components.length; i++) {
24646 var component = address_components[i];
24647 if (component.types.indexOf("postal_code") >= 0) {
24648 result.postalCode = component.short_name;
24649 } else if (component.types.indexOf("street_number") >= 0) {
24650 result.streetNumber = component.short_name;
24651 } else if (component.types.indexOf("route") >= 0) {
24652 result.streetName = component.short_name;
24653 } else if (component.types.indexOf("neighborhood") >= 0) {
24654 result.city = component.short_name;
24655 } else if (component.types.indexOf("locality") >= 0) {
24656 result.city = component.short_name;
24657 } else if (component.types.indexOf("sublocality") >= 0) {
24658 result.district = component.short_name;
24659 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24660 result.stateOrProvince = component.short_name;
24661 } else if (component.types.indexOf("country") >= 0) {
24662 result.country = component.short_name;
24666 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24667 result.addressLine2 = "";
24671 setZoomLevel: function(zoom)
24673 this.gMapContext.map.setZoom(zoom);
24686 this.fireEvent('show', this);
24697 this.fireEvent('hide', this);
24702 Roo.apply(Roo.bootstrap.LocationPicker, {
24704 OverlayView : function(map, options)
24706 options = options || {};
24720 * @class Roo.bootstrap.Alert
24721 * @extends Roo.bootstrap.Component
24722 * Bootstrap Alert class
24723 * @cfg {String} title The title of alert
24724 * @cfg {String} html The content of alert
24725 * @cfg {String} weight ( success | info | warning | danger )
24726 * @cfg {String} faicon font-awesomeicon
24729 * Create a new alert
24730 * @param {Object} config The config object
24734 Roo.bootstrap.Alert = function(config){
24735 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24739 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24746 getAutoCreate : function()
24755 cls : 'roo-alert-icon'
24760 cls : 'roo-alert-title',
24765 cls : 'roo-alert-text',
24772 cfg.cn[0].cls += ' fa ' + this.faicon;
24776 cfg.cls += ' alert-' + this.weight;
24782 initEvents: function()
24784 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24787 setTitle : function(str)
24789 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24792 setText : function(str)
24794 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24797 setWeight : function(weight)
24800 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24803 this.weight = weight;
24805 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24808 setIcon : function(icon)
24811 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24814 this.faicon = icon;
24816 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24837 * @class Roo.bootstrap.UploadCropbox
24838 * @extends Roo.bootstrap.Component
24839 * Bootstrap UploadCropbox class
24840 * @cfg {String} emptyText show when image has been loaded
24841 * @cfg {String} rotateNotify show when image too small to rotate
24842 * @cfg {Number} errorTimeout default 3000
24843 * @cfg {Number} minWidth default 300
24844 * @cfg {Number} minHeight default 300
24845 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24846 * @cfg {Boolean} isDocument (true|false) default false
24847 * @cfg {String} url action url
24848 * @cfg {String} paramName default 'imageUpload'
24849 * @cfg {String} method default POST
24850 * @cfg {Boolean} loadMask (true|false) default true
24851 * @cfg {Boolean} loadingText default 'Loading...'
24854 * Create a new UploadCropbox
24855 * @param {Object} config The config object
24858 Roo.bootstrap.UploadCropbox = function(config){
24859 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24863 * @event beforeselectfile
24864 * Fire before select file
24865 * @param {Roo.bootstrap.UploadCropbox} this
24867 "beforeselectfile" : true,
24870 * Fire after initEvent
24871 * @param {Roo.bootstrap.UploadCropbox} this
24876 * Fire after initEvent
24877 * @param {Roo.bootstrap.UploadCropbox} this
24878 * @param {String} data
24883 * Fire when preparing the file data
24884 * @param {Roo.bootstrap.UploadCropbox} this
24885 * @param {Object} file
24890 * Fire when get exception
24891 * @param {Roo.bootstrap.UploadCropbox} this
24892 * @param {XMLHttpRequest} xhr
24894 "exception" : true,
24896 * @event beforeloadcanvas
24897 * Fire before load the canvas
24898 * @param {Roo.bootstrap.UploadCropbox} this
24899 * @param {String} src
24901 "beforeloadcanvas" : true,
24904 * Fire when trash image
24905 * @param {Roo.bootstrap.UploadCropbox} this
24910 * Fire when download the image
24911 * @param {Roo.bootstrap.UploadCropbox} this
24915 * @event footerbuttonclick
24916 * Fire when footerbuttonclick
24917 * @param {Roo.bootstrap.UploadCropbox} this
24918 * @param {String} type
24920 "footerbuttonclick" : true,
24924 * @param {Roo.bootstrap.UploadCropbox} this
24929 * Fire when rotate the image
24930 * @param {Roo.bootstrap.UploadCropbox} this
24931 * @param {String} pos
24936 * Fire when inspect the file
24937 * @param {Roo.bootstrap.UploadCropbox} this
24938 * @param {Object} file
24943 * Fire when xhr upload the file
24944 * @param {Roo.bootstrap.UploadCropbox} this
24945 * @param {Object} data
24950 * Fire when arrange the file data
24951 * @param {Roo.bootstrap.UploadCropbox} this
24952 * @param {Object} formData
24957 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24960 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
24962 emptyText : 'Click to upload image',
24963 rotateNotify : 'Image is too small to rotate',
24964 errorTimeout : 3000,
24978 cropType : 'image/jpeg',
24980 canvasLoaded : false,
24981 isDocument : false,
24983 paramName : 'imageUpload',
24985 loadingText : 'Loading...',
24988 getAutoCreate : function()
24992 cls : 'roo-upload-cropbox',
24996 cls : 'roo-upload-cropbox-selector',
25001 cls : 'roo-upload-cropbox-body',
25002 style : 'cursor:pointer',
25006 cls : 'roo-upload-cropbox-preview'
25010 cls : 'roo-upload-cropbox-thumb'
25014 cls : 'roo-upload-cropbox-empty-notify',
25015 html : this.emptyText
25019 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25020 html : this.rotateNotify
25026 cls : 'roo-upload-cropbox-footer',
25029 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25039 onRender : function(ct, position)
25041 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25043 if (this.buttons.length) {
25045 Roo.each(this.buttons, function(bb) {
25047 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25049 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25055 this.maskEl = this.el;
25059 initEvents : function()
25061 this.urlAPI = (window.createObjectURL && window) ||
25062 (window.URL && URL.revokeObjectURL && URL) ||
25063 (window.webkitURL && webkitURL);
25065 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25066 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25068 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25069 this.selectorEl.hide();
25071 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25072 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25074 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25075 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25076 this.thumbEl.hide();
25078 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25079 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25081 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25082 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25083 this.errorEl.hide();
25085 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25086 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25087 this.footerEl.hide();
25089 this.setThumbBoxSize();
25095 this.fireEvent('initial', this);
25102 window.addEventListener("resize", function() { _this.resize(); } );
25104 this.bodyEl.on('click', this.beforeSelectFile, this);
25107 this.bodyEl.on('touchstart', this.onTouchStart, this);
25108 this.bodyEl.on('touchmove', this.onTouchMove, this);
25109 this.bodyEl.on('touchend', this.onTouchEnd, this);
25113 this.bodyEl.on('mousedown', this.onMouseDown, this);
25114 this.bodyEl.on('mousemove', this.onMouseMove, this);
25115 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25116 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25117 Roo.get(document).on('mouseup', this.onMouseUp, this);
25120 this.selectorEl.on('change', this.onFileSelected, this);
25126 this.baseScale = 1;
25128 this.baseRotate = 1;
25129 this.dragable = false;
25130 this.pinching = false;
25133 this.cropData = false;
25134 this.notifyEl.dom.innerHTML = this.emptyText;
25136 this.selectorEl.dom.value = '';
25140 resize : function()
25142 if(this.fireEvent('resize', this) != false){
25143 this.setThumbBoxPosition();
25144 this.setCanvasPosition();
25148 onFooterButtonClick : function(e, el, o, type)
25151 case 'rotate-left' :
25152 this.onRotateLeft(e);
25154 case 'rotate-right' :
25155 this.onRotateRight(e);
25158 this.beforeSelectFile(e);
25173 this.fireEvent('footerbuttonclick', this, type);
25176 beforeSelectFile : function(e)
25178 e.preventDefault();
25180 if(this.fireEvent('beforeselectfile', this) != false){
25181 this.selectorEl.dom.click();
25185 onFileSelected : function(e)
25187 e.preventDefault();
25189 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25193 var file = this.selectorEl.dom.files[0];
25195 if(this.fireEvent('inspect', this, file) != false){
25196 this.prepare(file);
25201 trash : function(e)
25203 this.fireEvent('trash', this);
25206 download : function(e)
25208 this.fireEvent('download', this);
25211 loadCanvas : function(src)
25213 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25217 this.imageEl = document.createElement('img');
25221 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25223 this.imageEl.src = src;
25227 onLoadCanvas : function()
25229 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25230 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25232 this.bodyEl.un('click', this.beforeSelectFile, this);
25234 this.notifyEl.hide();
25235 this.thumbEl.show();
25236 this.footerEl.show();
25238 this.baseRotateLevel();
25240 if(this.isDocument){
25241 this.setThumbBoxSize();
25244 this.setThumbBoxPosition();
25246 this.baseScaleLevel();
25252 this.canvasLoaded = true;
25255 this.maskEl.unmask();
25260 setCanvasPosition : function()
25262 if(!this.canvasEl){
25266 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25267 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25269 this.previewEl.setLeft(pw);
25270 this.previewEl.setTop(ph);
25274 onMouseDown : function(e)
25278 this.dragable = true;
25279 this.pinching = false;
25281 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25282 this.dragable = false;
25286 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25287 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25291 onMouseMove : function(e)
25295 if(!this.canvasLoaded){
25299 if (!this.dragable){
25303 var minX = Math.ceil(this.thumbEl.getLeft(true));
25304 var minY = Math.ceil(this.thumbEl.getTop(true));
25306 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25307 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25309 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25310 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25312 x = x - this.mouseX;
25313 y = y - this.mouseY;
25315 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25316 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25318 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25319 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25321 this.previewEl.setLeft(bgX);
25322 this.previewEl.setTop(bgY);
25324 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25325 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25328 onMouseUp : function(e)
25332 this.dragable = false;
25335 onMouseWheel : function(e)
25339 this.startScale = this.scale;
25341 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25343 if(!this.zoomable()){
25344 this.scale = this.startScale;
25353 zoomable : function()
25355 var minScale = this.thumbEl.getWidth() / this.minWidth;
25357 if(this.minWidth < this.minHeight){
25358 minScale = this.thumbEl.getHeight() / this.minHeight;
25361 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25362 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25366 (this.rotate == 0 || this.rotate == 180) &&
25368 width > this.imageEl.OriginWidth ||
25369 height > this.imageEl.OriginHeight ||
25370 (width < this.minWidth && height < this.minHeight)
25378 (this.rotate == 90 || this.rotate == 270) &&
25380 width > this.imageEl.OriginWidth ||
25381 height > this.imageEl.OriginHeight ||
25382 (width < this.minHeight && height < this.minWidth)
25389 !this.isDocument &&
25390 (this.rotate == 0 || this.rotate == 180) &&
25392 width < this.minWidth ||
25393 width > this.imageEl.OriginWidth ||
25394 height < this.minHeight ||
25395 height > this.imageEl.OriginHeight
25402 !this.isDocument &&
25403 (this.rotate == 90 || this.rotate == 270) &&
25405 width < this.minHeight ||
25406 width > this.imageEl.OriginWidth ||
25407 height < this.minWidth ||
25408 height > this.imageEl.OriginHeight
25418 onRotateLeft : function(e)
25420 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25422 var minScale = this.thumbEl.getWidth() / this.minWidth;
25424 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25425 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25427 this.startScale = this.scale;
25429 while (this.getScaleLevel() < minScale){
25431 this.scale = this.scale + 1;
25433 if(!this.zoomable()){
25438 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25439 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25444 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25451 this.scale = this.startScale;
25453 this.onRotateFail();
25458 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25460 if(this.isDocument){
25461 this.setThumbBoxSize();
25462 this.setThumbBoxPosition();
25463 this.setCanvasPosition();
25468 this.fireEvent('rotate', this, 'left');
25472 onRotateRight : function(e)
25474 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25476 var minScale = this.thumbEl.getWidth() / this.minWidth;
25478 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25479 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25481 this.startScale = this.scale;
25483 while (this.getScaleLevel() < minScale){
25485 this.scale = this.scale + 1;
25487 if(!this.zoomable()){
25492 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25493 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25498 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25505 this.scale = this.startScale;
25507 this.onRotateFail();
25512 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25514 if(this.isDocument){
25515 this.setThumbBoxSize();
25516 this.setThumbBoxPosition();
25517 this.setCanvasPosition();
25522 this.fireEvent('rotate', this, 'right');
25525 onRotateFail : function()
25527 this.errorEl.show(true);
25531 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25536 this.previewEl.dom.innerHTML = '';
25538 var canvasEl = document.createElement("canvas");
25540 var contextEl = canvasEl.getContext("2d");
25542 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25543 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25544 var center = this.imageEl.OriginWidth / 2;
25546 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25547 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25548 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25549 center = this.imageEl.OriginHeight / 2;
25552 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25554 contextEl.translate(center, center);
25555 contextEl.rotate(this.rotate * Math.PI / 180);
25557 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25559 this.canvasEl = document.createElement("canvas");
25561 this.contextEl = this.canvasEl.getContext("2d");
25563 switch (this.rotate) {
25566 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25567 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25569 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25574 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25575 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25577 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25578 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);
25582 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25587 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25588 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25590 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25591 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);
25595 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);
25600 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25601 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25603 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25604 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25608 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);
25615 this.previewEl.appendChild(this.canvasEl);
25617 this.setCanvasPosition();
25622 if(!this.canvasLoaded){
25626 var imageCanvas = document.createElement("canvas");
25628 var imageContext = imageCanvas.getContext("2d");
25630 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25631 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25633 var center = imageCanvas.width / 2;
25635 imageContext.translate(center, center);
25637 imageContext.rotate(this.rotate * Math.PI / 180);
25639 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25641 var canvas = document.createElement("canvas");
25643 var context = canvas.getContext("2d");
25645 canvas.width = this.minWidth;
25646 canvas.height = this.minHeight;
25648 switch (this.rotate) {
25651 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25652 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25654 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25655 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25657 var targetWidth = this.minWidth - 2 * x;
25658 var targetHeight = this.minHeight - 2 * y;
25662 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25663 scale = targetWidth / width;
25666 if(x > 0 && y == 0){
25667 scale = targetHeight / height;
25670 if(x > 0 && y > 0){
25671 scale = targetWidth / width;
25673 if(width < height){
25674 scale = targetHeight / height;
25678 context.scale(scale, scale);
25680 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25681 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25683 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25684 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25686 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25691 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25692 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25694 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25695 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25697 var targetWidth = this.minWidth - 2 * x;
25698 var targetHeight = this.minHeight - 2 * y;
25702 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25703 scale = targetWidth / width;
25706 if(x > 0 && y == 0){
25707 scale = targetHeight / height;
25710 if(x > 0 && y > 0){
25711 scale = targetWidth / width;
25713 if(width < height){
25714 scale = targetHeight / height;
25718 context.scale(scale, scale);
25720 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25721 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25723 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25724 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25726 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25728 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25733 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25734 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25736 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25737 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25739 var targetWidth = this.minWidth - 2 * x;
25740 var targetHeight = this.minHeight - 2 * y;
25744 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25745 scale = targetWidth / width;
25748 if(x > 0 && y == 0){
25749 scale = targetHeight / height;
25752 if(x > 0 && y > 0){
25753 scale = targetWidth / width;
25755 if(width < height){
25756 scale = targetHeight / height;
25760 context.scale(scale, scale);
25762 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25763 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25765 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25766 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25768 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25769 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25771 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25776 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25777 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25779 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25780 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25782 var targetWidth = this.minWidth - 2 * x;
25783 var targetHeight = this.minHeight - 2 * y;
25787 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25788 scale = targetWidth / width;
25791 if(x > 0 && y == 0){
25792 scale = targetHeight / height;
25795 if(x > 0 && y > 0){
25796 scale = targetWidth / width;
25798 if(width < height){
25799 scale = targetHeight / height;
25803 context.scale(scale, scale);
25805 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25806 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25808 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25809 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25811 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25813 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25820 this.cropData = canvas.toDataURL(this.cropType);
25822 if(this.fireEvent('crop', this, this.cropData) !== false){
25823 this.process(this.file, this.cropData);
25830 setThumbBoxSize : function()
25834 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25835 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25836 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25838 this.minWidth = width;
25839 this.minHeight = height;
25841 if(this.rotate == 90 || this.rotate == 270){
25842 this.minWidth = height;
25843 this.minHeight = width;
25848 width = Math.ceil(this.minWidth * height / this.minHeight);
25850 if(this.minWidth > this.minHeight){
25852 height = Math.ceil(this.minHeight * width / this.minWidth);
25855 this.thumbEl.setStyle({
25856 width : width + 'px',
25857 height : height + 'px'
25864 setThumbBoxPosition : function()
25866 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25867 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25869 this.thumbEl.setLeft(x);
25870 this.thumbEl.setTop(y);
25874 baseRotateLevel : function()
25876 this.baseRotate = 1;
25879 typeof(this.exif) != 'undefined' &&
25880 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25881 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25883 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25886 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25890 baseScaleLevel : function()
25894 if(this.isDocument){
25896 if(this.baseRotate == 6 || this.baseRotate == 8){
25898 height = this.thumbEl.getHeight();
25899 this.baseScale = height / this.imageEl.OriginWidth;
25901 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25902 width = this.thumbEl.getWidth();
25903 this.baseScale = width / this.imageEl.OriginHeight;
25909 height = this.thumbEl.getHeight();
25910 this.baseScale = height / this.imageEl.OriginHeight;
25912 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25913 width = this.thumbEl.getWidth();
25914 this.baseScale = width / this.imageEl.OriginWidth;
25920 if(this.baseRotate == 6 || this.baseRotate == 8){
25922 width = this.thumbEl.getHeight();
25923 this.baseScale = width / this.imageEl.OriginHeight;
25925 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25926 height = this.thumbEl.getWidth();
25927 this.baseScale = height / this.imageEl.OriginHeight;
25930 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25931 height = this.thumbEl.getWidth();
25932 this.baseScale = height / this.imageEl.OriginHeight;
25934 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25935 width = this.thumbEl.getHeight();
25936 this.baseScale = width / this.imageEl.OriginWidth;
25943 width = this.thumbEl.getWidth();
25944 this.baseScale = width / this.imageEl.OriginWidth;
25946 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25947 height = this.thumbEl.getHeight();
25948 this.baseScale = height / this.imageEl.OriginHeight;
25951 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25953 height = this.thumbEl.getHeight();
25954 this.baseScale = height / this.imageEl.OriginHeight;
25956 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25957 width = this.thumbEl.getWidth();
25958 this.baseScale = width / this.imageEl.OriginWidth;
25966 getScaleLevel : function()
25968 return this.baseScale * Math.pow(1.1, this.scale);
25971 onTouchStart : function(e)
25973 if(!this.canvasLoaded){
25974 this.beforeSelectFile(e);
25978 var touches = e.browserEvent.touches;
25984 if(touches.length == 1){
25985 this.onMouseDown(e);
25989 if(touches.length != 2){
25995 for(var i = 0, finger; finger = touches[i]; i++){
25996 coords.push(finger.pageX, finger.pageY);
25999 var x = Math.pow(coords[0] - coords[2], 2);
26000 var y = Math.pow(coords[1] - coords[3], 2);
26002 this.startDistance = Math.sqrt(x + y);
26004 this.startScale = this.scale;
26006 this.pinching = true;
26007 this.dragable = false;
26011 onTouchMove : function(e)
26013 if(!this.pinching && !this.dragable){
26017 var touches = e.browserEvent.touches;
26024 this.onMouseMove(e);
26030 for(var i = 0, finger; finger = touches[i]; i++){
26031 coords.push(finger.pageX, finger.pageY);
26034 var x = Math.pow(coords[0] - coords[2], 2);
26035 var y = Math.pow(coords[1] - coords[3], 2);
26037 this.endDistance = Math.sqrt(x + y);
26039 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26041 if(!this.zoomable()){
26042 this.scale = this.startScale;
26050 onTouchEnd : function(e)
26052 this.pinching = false;
26053 this.dragable = false;
26057 process : function(file, crop)
26060 this.maskEl.mask(this.loadingText);
26063 this.xhr = new XMLHttpRequest();
26065 file.xhr = this.xhr;
26067 this.xhr.open(this.method, this.url, true);
26070 "Accept": "application/json",
26071 "Cache-Control": "no-cache",
26072 "X-Requested-With": "XMLHttpRequest"
26075 for (var headerName in headers) {
26076 var headerValue = headers[headerName];
26078 this.xhr.setRequestHeader(headerName, headerValue);
26084 this.xhr.onload = function()
26086 _this.xhrOnLoad(_this.xhr);
26089 this.xhr.onerror = function()
26091 _this.xhrOnError(_this.xhr);
26094 var formData = new FormData();
26096 formData.append('returnHTML', 'NO');
26099 formData.append('crop', crop);
26102 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26103 formData.append(this.paramName, file, file.name);
26106 if(typeof(file.filename) != 'undefined'){
26107 formData.append('filename', file.filename);
26110 if(typeof(file.mimetype) != 'undefined'){
26111 formData.append('mimetype', file.mimetype);
26114 if(this.fireEvent('arrange', this, formData) != false){
26115 this.xhr.send(formData);
26119 xhrOnLoad : function(xhr)
26122 this.maskEl.unmask();
26125 if (xhr.readyState !== 4) {
26126 this.fireEvent('exception', this, xhr);
26130 var response = Roo.decode(xhr.responseText);
26132 if(!response.success){
26133 this.fireEvent('exception', this, xhr);
26137 var response = Roo.decode(xhr.responseText);
26139 this.fireEvent('upload', this, response);
26143 xhrOnError : function()
26146 this.maskEl.unmask();
26149 Roo.log('xhr on error');
26151 var response = Roo.decode(xhr.responseText);
26157 prepare : function(file)
26160 this.maskEl.mask(this.loadingText);
26166 if(typeof(file) === 'string'){
26167 this.loadCanvas(file);
26171 if(!file || !this.urlAPI){
26176 this.cropType = file.type;
26180 if(this.fireEvent('prepare', this, this.file) != false){
26182 var reader = new FileReader();
26184 reader.onload = function (e) {
26185 if (e.target.error) {
26186 Roo.log(e.target.error);
26190 var buffer = e.target.result,
26191 dataView = new DataView(buffer),
26193 maxOffset = dataView.byteLength - 4,
26197 if (dataView.getUint16(0) === 0xffd8) {
26198 while (offset < maxOffset) {
26199 markerBytes = dataView.getUint16(offset);
26201 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26202 markerLength = dataView.getUint16(offset + 2) + 2;
26203 if (offset + markerLength > dataView.byteLength) {
26204 Roo.log('Invalid meta data: Invalid segment size.');
26208 if(markerBytes == 0xffe1){
26209 _this.parseExifData(
26216 offset += markerLength;
26226 var url = _this.urlAPI.createObjectURL(_this.file);
26228 _this.loadCanvas(url);
26233 reader.readAsArrayBuffer(this.file);
26239 parseExifData : function(dataView, offset, length)
26241 var tiffOffset = offset + 10,
26245 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26246 // No Exif data, might be XMP data instead
26250 // Check for the ASCII code for "Exif" (0x45786966):
26251 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26252 // No Exif data, might be XMP data instead
26255 if (tiffOffset + 8 > dataView.byteLength) {
26256 Roo.log('Invalid Exif data: Invalid segment size.');
26259 // Check for the two null bytes:
26260 if (dataView.getUint16(offset + 8) !== 0x0000) {
26261 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26264 // Check the byte alignment:
26265 switch (dataView.getUint16(tiffOffset)) {
26267 littleEndian = true;
26270 littleEndian = false;
26273 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26276 // Check for the TIFF tag marker (0x002A):
26277 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26278 Roo.log('Invalid Exif data: Missing TIFF marker.');
26281 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26282 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26284 this.parseExifTags(
26287 tiffOffset + dirOffset,
26292 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26297 if (dirOffset + 6 > dataView.byteLength) {
26298 Roo.log('Invalid Exif data: Invalid directory offset.');
26301 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26302 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26303 if (dirEndOffset + 4 > dataView.byteLength) {
26304 Roo.log('Invalid Exif data: Invalid directory size.');
26307 for (i = 0; i < tagsNumber; i += 1) {
26311 dirOffset + 2 + 12 * i, // tag offset
26315 // Return the offset to the next directory:
26316 return dataView.getUint32(dirEndOffset, littleEndian);
26319 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26321 var tag = dataView.getUint16(offset, littleEndian);
26323 this.exif[tag] = this.getExifValue(
26327 dataView.getUint16(offset + 2, littleEndian), // tag type
26328 dataView.getUint32(offset + 4, littleEndian), // tag length
26333 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26335 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26344 Roo.log('Invalid Exif data: Invalid tag type.');
26348 tagSize = tagType.size * length;
26349 // Determine if the value is contained in the dataOffset bytes,
26350 // or if the value at the dataOffset is a pointer to the actual data:
26351 dataOffset = tagSize > 4 ?
26352 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26353 if (dataOffset + tagSize > dataView.byteLength) {
26354 Roo.log('Invalid Exif data: Invalid data offset.');
26357 if (length === 1) {
26358 return tagType.getValue(dataView, dataOffset, littleEndian);
26361 for (i = 0; i < length; i += 1) {
26362 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26365 if (tagType.ascii) {
26367 // Concatenate the chars:
26368 for (i = 0; i < values.length; i += 1) {
26370 // Ignore the terminating NULL byte(s):
26371 if (c === '\u0000') {
26383 Roo.apply(Roo.bootstrap.UploadCropbox, {
26385 'Orientation': 0x0112
26389 1: 0, //'top-left',
26391 3: 180, //'bottom-right',
26392 // 4: 'bottom-left',
26394 6: 90, //'right-top',
26395 // 7: 'right-bottom',
26396 8: 270 //'left-bottom'
26400 // byte, 8-bit unsigned int:
26402 getValue: function (dataView, dataOffset) {
26403 return dataView.getUint8(dataOffset);
26407 // ascii, 8-bit byte:
26409 getValue: function (dataView, dataOffset) {
26410 return String.fromCharCode(dataView.getUint8(dataOffset));
26415 // short, 16 bit int:
26417 getValue: function (dataView, dataOffset, littleEndian) {
26418 return dataView.getUint16(dataOffset, littleEndian);
26422 // long, 32 bit int:
26424 getValue: function (dataView, dataOffset, littleEndian) {
26425 return dataView.getUint32(dataOffset, littleEndian);
26429 // rational = two long values, first is numerator, second is denominator:
26431 getValue: function (dataView, dataOffset, littleEndian) {
26432 return dataView.getUint32(dataOffset, littleEndian) /
26433 dataView.getUint32(dataOffset + 4, littleEndian);
26437 // slong, 32 bit signed int:
26439 getValue: function (dataView, dataOffset, littleEndian) {
26440 return dataView.getInt32(dataOffset, littleEndian);
26444 // srational, two slongs, first is numerator, second is denominator:
26446 getValue: function (dataView, dataOffset, littleEndian) {
26447 return dataView.getInt32(dataOffset, littleEndian) /
26448 dataView.getInt32(dataOffset + 4, littleEndian);
26458 cls : 'btn-group roo-upload-cropbox-rotate-left',
26459 action : 'rotate-left',
26463 cls : 'btn btn-default',
26464 html : '<i class="fa fa-undo"></i>'
26470 cls : 'btn-group roo-upload-cropbox-picture',
26471 action : 'picture',
26475 cls : 'btn btn-default',
26476 html : '<i class="fa fa-picture-o"></i>'
26482 cls : 'btn-group roo-upload-cropbox-rotate-right',
26483 action : 'rotate-right',
26487 cls : 'btn btn-default',
26488 html : '<i class="fa fa-repeat"></i>'
26496 cls : 'btn-group roo-upload-cropbox-rotate-left',
26497 action : 'rotate-left',
26501 cls : 'btn btn-default',
26502 html : '<i class="fa fa-undo"></i>'
26508 cls : 'btn-group roo-upload-cropbox-download',
26509 action : 'download',
26513 cls : 'btn btn-default',
26514 html : '<i class="fa fa-download"></i>'
26520 cls : 'btn-group roo-upload-cropbox-crop',
26525 cls : 'btn btn-default',
26526 html : '<i class="fa fa-crop"></i>'
26532 cls : 'btn-group roo-upload-cropbox-trash',
26537 cls : 'btn btn-default',
26538 html : '<i class="fa fa-trash"></i>'
26544 cls : 'btn-group roo-upload-cropbox-rotate-right',
26545 action : 'rotate-right',
26549 cls : 'btn btn-default',
26550 html : '<i class="fa fa-repeat"></i>'
26558 cls : 'btn-group roo-upload-cropbox-rotate-left',
26559 action : 'rotate-left',
26563 cls : 'btn btn-default',
26564 html : '<i class="fa fa-undo"></i>'
26570 cls : 'btn-group roo-upload-cropbox-rotate-right',
26571 action : 'rotate-right',
26575 cls : 'btn btn-default',
26576 html : '<i class="fa fa-repeat"></i>'
26589 * @class Roo.bootstrap.DocumentManager
26590 * @extends Roo.bootstrap.Component
26591 * Bootstrap DocumentManager class
26592 * @cfg {String} paramName default 'imageUpload'
26593 * @cfg {String} method default POST
26594 * @cfg {String} url action url
26595 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26596 * @cfg {Boolean} multiple multiple upload default true
26597 * @cfg {Number} thumbSize default 300
26598 * @cfg {String} fieldLabel
26599 * @cfg {Number} labelWidth default 4
26600 * @cfg {String} labelAlign (left|top) default left
26601 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26604 * Create a new DocumentManager
26605 * @param {Object} config The config object
26608 Roo.bootstrap.DocumentManager = function(config){
26609 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26614 * Fire when initial the DocumentManager
26615 * @param {Roo.bootstrap.DocumentManager} this
26620 * inspect selected file
26621 * @param {Roo.bootstrap.DocumentManager} this
26622 * @param {File} file
26627 * Fire when xhr load exception
26628 * @param {Roo.bootstrap.DocumentManager} this
26629 * @param {XMLHttpRequest} xhr
26631 "exception" : true,
26634 * prepare the form data
26635 * @param {Roo.bootstrap.DocumentManager} this
26636 * @param {Object} formData
26641 * Fire when remove the file
26642 * @param {Roo.bootstrap.DocumentManager} this
26643 * @param {Object} file
26648 * Fire after refresh the file
26649 * @param {Roo.bootstrap.DocumentManager} this
26654 * Fire after click the image
26655 * @param {Roo.bootstrap.DocumentManager} this
26656 * @param {Object} file
26661 * Fire when upload a image and editable set to true
26662 * @param {Roo.bootstrap.DocumentManager} this
26663 * @param {Object} file
26667 * @event beforeselectfile
26668 * Fire before select file
26669 * @param {Roo.bootstrap.DocumentManager} this
26671 "beforeselectfile" : true,
26674 * Fire before process file
26675 * @param {Roo.bootstrap.DocumentManager} this
26676 * @param {Object} file
26683 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26692 paramName : 'imageUpload',
26695 labelAlign : 'left',
26702 getAutoCreate : function()
26704 var managerWidget = {
26706 cls : 'roo-document-manager',
26710 cls : 'roo-document-manager-selector',
26715 cls : 'roo-document-manager-uploader',
26719 cls : 'roo-document-manager-upload-btn',
26720 html : '<i class="fa fa-plus"></i>'
26731 cls : 'column col-md-12',
26736 if(this.fieldLabel.length){
26741 cls : 'column col-md-12',
26742 html : this.fieldLabel
26746 cls : 'column col-md-12',
26751 if(this.labelAlign == 'left'){
26755 cls : 'column col-md-' + this.labelWidth,
26756 html : this.fieldLabel
26760 cls : 'column col-md-' + (12 - this.labelWidth),
26770 cls : 'row clearfix',
26778 initEvents : function()
26780 this.managerEl = this.el.select('.roo-document-manager', true).first();
26781 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26783 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26784 this.selectorEl.hide();
26787 this.selectorEl.attr('multiple', 'multiple');
26790 this.selectorEl.on('change', this.onFileSelected, this);
26792 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26793 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26795 this.uploader.on('click', this.onUploaderClick, this);
26797 this.renderProgressDialog();
26801 window.addEventListener("resize", function() { _this.refresh(); } );
26803 this.fireEvent('initial', this);
26806 renderProgressDialog : function()
26810 this.progressDialog = new Roo.bootstrap.Modal({
26811 cls : 'roo-document-manager-progress-dialog',
26812 allow_close : false,
26822 btnclick : function() {
26823 _this.uploadCancel();
26829 this.progressDialog.render(Roo.get(document.body));
26831 this.progress = new Roo.bootstrap.Progress({
26832 cls : 'roo-document-manager-progress',
26837 this.progress.render(this.progressDialog.getChildContainer());
26839 this.progressBar = new Roo.bootstrap.ProgressBar({
26840 cls : 'roo-document-manager-progress-bar',
26843 aria_valuemax : 12,
26847 this.progressBar.render(this.progress.getChildContainer());
26850 onUploaderClick : function(e)
26852 e.preventDefault();
26854 if(this.fireEvent('beforeselectfile', this) != false){
26855 this.selectorEl.dom.click();
26860 onFileSelected : function(e)
26862 e.preventDefault();
26864 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26868 Roo.each(this.selectorEl.dom.files, function(file){
26869 if(this.fireEvent('inspect', this, file) != false){
26870 this.files.push(file);
26880 this.selectorEl.dom.value = '';
26882 if(!this.files.length){
26886 if(this.boxes > 0 && this.files.length > this.boxes){
26887 this.files = this.files.slice(0, this.boxes);
26890 this.uploader.show();
26892 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26893 this.uploader.hide();
26902 Roo.each(this.files, function(file){
26904 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26905 var f = this.renderPreview(file);
26910 if(file.type.indexOf('image') != -1){
26911 this.delegates.push(
26913 _this.process(file);
26914 }).createDelegate(this)
26922 _this.process(file);
26923 }).createDelegate(this)
26928 this.files = files;
26930 this.delegates = this.delegates.concat(docs);
26932 if(!this.delegates.length){
26937 this.progressBar.aria_valuemax = this.delegates.length;
26944 arrange : function()
26946 if(!this.delegates.length){
26947 this.progressDialog.hide();
26952 var delegate = this.delegates.shift();
26954 this.progressDialog.show();
26956 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26958 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26963 refresh : function()
26965 this.uploader.show();
26967 if(this.boxes > 0 && this.files.length > this.boxes - 1){
26968 this.uploader.hide();
26971 Roo.isTouch ? this.closable(false) : this.closable(true);
26973 this.fireEvent('refresh', this);
26976 onRemove : function(e, el, o)
26978 e.preventDefault();
26980 this.fireEvent('remove', this, o);
26984 remove : function(o)
26988 Roo.each(this.files, function(file){
26989 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26998 this.files = files;
27005 Roo.each(this.files, function(file){
27010 file.target.remove();
27019 onClick : function(e, el, o)
27021 e.preventDefault();
27023 this.fireEvent('click', this, o);
27027 closable : function(closable)
27029 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27031 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27043 xhrOnLoad : function(xhr)
27045 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27049 if (xhr.readyState !== 4) {
27051 this.fireEvent('exception', this, xhr);
27055 var response = Roo.decode(xhr.responseText);
27057 if(!response.success){
27059 this.fireEvent('exception', this, xhr);
27063 var file = this.renderPreview(response.data);
27065 this.files.push(file);
27071 xhrOnError : function(xhr)
27073 Roo.log('xhr on error');
27075 var response = Roo.decode(xhr.responseText);
27082 process : function(file)
27084 if(this.fireEvent('process', this, file) !== false){
27085 if(this.editable && file.type.indexOf('image') != -1){
27086 this.fireEvent('edit', this, file);
27090 this.uploadStart(file, false);
27097 uploadStart : function(file, crop)
27099 this.xhr = new XMLHttpRequest();
27101 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27106 file.xhr = this.xhr;
27108 this.managerEl.createChild({
27110 cls : 'roo-document-manager-loading',
27114 tooltip : file.name,
27115 cls : 'roo-document-manager-thumb',
27116 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27122 this.xhr.open(this.method, this.url, true);
27125 "Accept": "application/json",
27126 "Cache-Control": "no-cache",
27127 "X-Requested-With": "XMLHttpRequest"
27130 for (var headerName in headers) {
27131 var headerValue = headers[headerName];
27133 this.xhr.setRequestHeader(headerName, headerValue);
27139 this.xhr.onload = function()
27141 _this.xhrOnLoad(_this.xhr);
27144 this.xhr.onerror = function()
27146 _this.xhrOnError(_this.xhr);
27149 var formData = new FormData();
27151 formData.append('returnHTML', 'NO');
27154 formData.append('crop', crop);
27157 formData.append(this.paramName, file, file.name);
27159 if(this.fireEvent('prepare', this, formData) != false){
27160 this.xhr.send(formData);
27164 uploadCancel : function()
27171 this.delegates = [];
27173 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27180 renderPreview : function(file)
27182 if(typeof(file.target) != 'undefined' && file.target){
27186 var previewEl = this.managerEl.createChild({
27188 cls : 'roo-document-manager-preview',
27192 tooltip : file.filename,
27193 cls : 'roo-document-manager-thumb',
27194 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27199 html : '<i class="fa fa-times-circle"></i>'
27204 var close = previewEl.select('button.close', true).first();
27206 close.on('click', this.onRemove, this, file);
27208 file.target = previewEl;
27210 var image = previewEl.select('img', true).first();
27214 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27216 image.on('click', this.onClick, this, file);
27222 onPreviewLoad : function(file, image)
27224 if(typeof(file.target) == 'undefined' || !file.target){
27228 var width = image.dom.naturalWidth || image.dom.width;
27229 var height = image.dom.naturalHeight || image.dom.height;
27231 if(width > height){
27232 file.target.addClass('wide');
27236 file.target.addClass('tall');
27241 uploadFromSource : function(file, crop)
27243 this.xhr = new XMLHttpRequest();
27245 this.managerEl.createChild({
27247 cls : 'roo-document-manager-loading',
27251 tooltip : file.name,
27252 cls : 'roo-document-manager-thumb',
27253 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27259 this.xhr.open(this.method, this.url, true);
27262 "Accept": "application/json",
27263 "Cache-Control": "no-cache",
27264 "X-Requested-With": "XMLHttpRequest"
27267 for (var headerName in headers) {
27268 var headerValue = headers[headerName];
27270 this.xhr.setRequestHeader(headerName, headerValue);
27276 this.xhr.onload = function()
27278 _this.xhrOnLoad(_this.xhr);
27281 this.xhr.onerror = function()
27283 _this.xhrOnError(_this.xhr);
27286 var formData = new FormData();
27288 formData.append('returnHTML', 'NO');
27290 formData.append('crop', crop);
27292 if(typeof(file.filename) != 'undefined'){
27293 formData.append('filename', file.filename);
27296 if(typeof(file.mimetype) != 'undefined'){
27297 formData.append('mimetype', file.mimetype);
27300 if(this.fireEvent('prepare', this, formData) != false){
27301 this.xhr.send(formData);
27311 * @class Roo.bootstrap.DocumentViewer
27312 * @extends Roo.bootstrap.Component
27313 * Bootstrap DocumentViewer class
27316 * Create a new DocumentViewer
27317 * @param {Object} config The config object
27320 Roo.bootstrap.DocumentViewer = function(config){
27321 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27326 * Fire after initEvent
27327 * @param {Roo.bootstrap.DocumentViewer} this
27333 * @param {Roo.bootstrap.DocumentViewer} this
27338 * Fire after trash button
27339 * @param {Roo.bootstrap.DocumentViewer} this
27346 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27348 getAutoCreate : function()
27352 cls : 'roo-document-viewer',
27356 cls : 'roo-document-viewer-body',
27360 cls : 'roo-document-viewer-thumb',
27364 cls : 'roo-document-viewer-image'
27372 cls : 'roo-document-viewer-footer',
27375 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27383 cls : 'btn btn-default roo-document-viewer-trash',
27384 html : '<i class="fa fa-trash"></i>'
27397 initEvents : function()
27400 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27401 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27403 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27404 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27406 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27407 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27409 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27410 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27412 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27413 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27415 this.bodyEl.on('click', this.onClick, this);
27417 this.trashBtn.on('click', this.onTrash, this);
27421 initial : function()
27423 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27426 this.fireEvent('initial', this);
27430 onClick : function(e)
27432 e.preventDefault();
27434 this.fireEvent('click', this);
27437 onTrash : function(e)
27439 e.preventDefault();
27441 this.fireEvent('trash', this);
27453 * @class Roo.bootstrap.NavProgressBar
27454 * @extends Roo.bootstrap.Component
27455 * Bootstrap NavProgressBar class
27458 * Create a new nav progress bar
27459 * @param {Object} config The config object
27462 Roo.bootstrap.NavProgressBar = function(config){
27463 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27465 this.bullets = this.bullets || [];
27467 // Roo.bootstrap.NavProgressBar.register(this);
27471 * Fires when the active item changes
27472 * @param {Roo.bootstrap.NavProgressBar} this
27473 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27474 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27481 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27486 getAutoCreate : function()
27488 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27492 cls : 'roo-navigation-bar-group',
27496 cls : 'roo-navigation-top-bar'
27500 cls : 'roo-navigation-bullets-bar',
27504 cls : 'roo-navigation-bar'
27511 cls : 'roo-navigation-bottom-bar'
27521 initEvents: function()
27526 onRender : function(ct, position)
27528 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27530 if(this.bullets.length){
27531 Roo.each(this.bullets, function(b){
27540 addItem : function(cfg)
27542 var item = new Roo.bootstrap.NavProgressItem(cfg);
27544 item.parentId = this.id;
27545 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27548 var top = new Roo.bootstrap.Element({
27550 cls : 'roo-navigation-bar-text'
27553 var bottom = new Roo.bootstrap.Element({
27555 cls : 'roo-navigation-bar-text'
27558 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27559 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27561 var topText = new Roo.bootstrap.Element({
27563 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27566 var bottomText = new Roo.bootstrap.Element({
27568 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27571 topText.onRender(top.el, null);
27572 bottomText.onRender(bottom.el, null);
27575 item.bottomEl = bottom;
27578 this.barItems.push(item);
27583 getActive : function()
27585 var active = false;
27587 Roo.each(this.barItems, function(v){
27589 if (!v.isActive()) {
27601 setActiveItem : function(item)
27605 Roo.each(this.barItems, function(v){
27606 if (v.rid == item.rid) {
27610 if (v.isActive()) {
27611 v.setActive(false);
27616 item.setActive(true);
27618 this.fireEvent('changed', this, item, prev);
27621 getBarItem: function(rid)
27625 Roo.each(this.barItems, function(e) {
27626 if (e.rid != rid) {
27637 indexOfItem : function(item)
27641 Roo.each(this.barItems, function(v, i){
27643 if (v.rid != item.rid) {
27654 setActiveNext : function()
27656 var i = this.indexOfItem(this.getActive());
27658 if (i > this.barItems.length) {
27662 this.setActiveItem(this.barItems[i+1]);
27665 setActivePrev : function()
27667 var i = this.indexOfItem(this.getActive());
27673 this.setActiveItem(this.barItems[i-1]);
27676 format : function()
27678 if(!this.barItems.length){
27682 var width = 100 / this.barItems.length;
27684 Roo.each(this.barItems, function(i){
27685 i.el.setStyle('width', width + '%');
27686 i.topEl.el.setStyle('width', width + '%');
27687 i.bottomEl.el.setStyle('width', width + '%');
27696 * Nav Progress Item
27701 * @class Roo.bootstrap.NavProgressItem
27702 * @extends Roo.bootstrap.Component
27703 * Bootstrap NavProgressItem class
27704 * @cfg {String} rid the reference id
27705 * @cfg {Boolean} active (true|false) Is item active default false
27706 * @cfg {Boolean} disabled (true|false) Is item active default false
27707 * @cfg {String} html
27708 * @cfg {String} position (top|bottom) text position default bottom
27709 * @cfg {String} icon show icon instead of number
27712 * Create a new NavProgressItem
27713 * @param {Object} config The config object
27715 Roo.bootstrap.NavProgressItem = function(config){
27716 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27721 * The raw click event for the entire grid.
27722 * @param {Roo.bootstrap.NavProgressItem} this
27723 * @param {Roo.EventObject} e
27730 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27736 position : 'bottom',
27739 getAutoCreate : function()
27741 var iconCls = 'roo-navigation-bar-item-icon';
27743 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27747 cls: 'roo-navigation-bar-item',
27757 cfg.cls += ' active';
27760 cfg.cls += ' disabled';
27766 disable : function()
27768 this.setDisabled(true);
27771 enable : function()
27773 this.setDisabled(false);
27776 initEvents: function()
27778 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27780 this.iconEl.on('click', this.onClick, this);
27783 onClick : function(e)
27785 e.preventDefault();
27791 if(this.fireEvent('click', this, e) === false){
27795 this.parent().setActiveItem(this);
27798 isActive: function ()
27800 return this.active;
27803 setActive : function(state)
27805 if(this.active == state){
27809 this.active = state;
27812 this.el.addClass('active');
27816 this.el.removeClass('active');
27821 setDisabled : function(state)
27823 if(this.disabled == state){
27827 this.disabled = state;
27830 this.el.addClass('disabled');
27834 this.el.removeClass('disabled');
27837 tooltipEl : function()
27839 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27852 * @class Roo.bootstrap.FieldLabel
27853 * @extends Roo.bootstrap.Component
27854 * Bootstrap FieldLabel class
27855 * @cfg {String} html contents of the element
27856 * @cfg {String} tag tag of the element default label
27857 * @cfg {String} cls class of the element
27858 * @cfg {String} target label target
27859 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27860 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27861 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27862 * @cfg {String} iconTooltip default "This field is required"
27865 * Create a new FieldLabel
27866 * @param {Object} config The config object
27869 Roo.bootstrap.FieldLabel = function(config){
27870 Roo.bootstrap.Element.superclass.constructor.call(this, config);
27875 * Fires after the field has been marked as invalid.
27876 * @param {Roo.form.FieldLabel} this
27877 * @param {String} msg The validation message
27882 * Fires after the field has been validated with no errors.
27883 * @param {Roo.form.FieldLabel} this
27889 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
27896 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27897 validClass : 'text-success fa fa-lg fa-check',
27898 iconTooltip : 'This field is required',
27900 getAutoCreate : function(){
27904 cls : 'roo-bootstrap-field-label ' + this.cls,
27910 tooltip : this.iconTooltip
27922 initEvents: function()
27924 Roo.bootstrap.Element.superclass.initEvents.call(this);
27926 this.iconEl = this.el.select('i', true).first();
27928 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27930 Roo.bootstrap.FieldLabel.register(this);
27934 * Mark this field as valid
27936 markValid : function()
27938 this.iconEl.show();
27940 this.iconEl.removeClass(this.invalidClass);
27942 this.iconEl.addClass(this.validClass);
27944 this.fireEvent('valid', this);
27948 * Mark this field as invalid
27949 * @param {String} msg The validation message
27951 markInvalid : function(msg)
27953 this.iconEl.show();
27955 this.iconEl.removeClass(this.validClass);
27957 this.iconEl.addClass(this.invalidClass);
27959 this.fireEvent('invalid', this, msg);
27965 Roo.apply(Roo.bootstrap.FieldLabel, {
27970 * register a FieldLabel Group
27971 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27973 register : function(label)
27975 if(this.groups.hasOwnProperty(label.target)){
27979 this.groups[label.target] = label;
27983 * fetch a FieldLabel Group based on the target
27984 * @param {string} target
27985 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27987 get: function(target) {
27988 if (typeof(this.groups[target]) == 'undefined') {
27992 return this.groups[target] ;
28001 * page DateSplitField.
28007 * @class Roo.bootstrap.DateSplitField
28008 * @extends Roo.bootstrap.Component
28009 * Bootstrap DateSplitField class
28010 * @cfg {string} fieldLabel - the label associated
28011 * @cfg {Number} labelWidth set the width of label (0-12)
28012 * @cfg {String} labelAlign (top|left)
28013 * @cfg {Boolean} dayAllowBlank (true|false) default false
28014 * @cfg {Boolean} monthAllowBlank (true|false) default false
28015 * @cfg {Boolean} yearAllowBlank (true|false) default false
28016 * @cfg {string} dayPlaceholder
28017 * @cfg {string} monthPlaceholder
28018 * @cfg {string} yearPlaceholder
28019 * @cfg {string} dayFormat default 'd'
28020 * @cfg {string} monthFormat default 'm'
28021 * @cfg {string} yearFormat default 'Y'
28025 * Create a new DateSplitField
28026 * @param {Object} config The config object
28029 Roo.bootstrap.DateSplitField = function(config){
28030 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28036 * getting the data of years
28037 * @param {Roo.bootstrap.DateSplitField} this
28038 * @param {Object} years
28043 * getting the data of days
28044 * @param {Roo.bootstrap.DateSplitField} this
28045 * @param {Object} days
28050 * Fires after the field has been marked as invalid.
28051 * @param {Roo.form.Field} this
28052 * @param {String} msg The validation message
28057 * Fires after the field has been validated with no errors.
28058 * @param {Roo.form.Field} this
28064 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28067 labelAlign : 'top',
28069 dayAllowBlank : false,
28070 monthAllowBlank : false,
28071 yearAllowBlank : false,
28072 dayPlaceholder : '',
28073 monthPlaceholder : '',
28074 yearPlaceholder : '',
28078 isFormField : true,
28080 getAutoCreate : function()
28084 cls : 'row roo-date-split-field-group',
28089 cls : 'form-hidden-field roo-date-split-field-group-value',
28095 if(this.fieldLabel){
28098 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28102 html : this.fieldLabel
28108 Roo.each(['day', 'month', 'year'], function(t){
28111 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28118 inputEl: function ()
28120 return this.el.select('.roo-date-split-field-group-value', true).first();
28123 onRender : function(ct, position)
28127 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28129 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28131 this.dayField = new Roo.bootstrap.ComboBox({
28132 allowBlank : this.dayAllowBlank,
28133 alwaysQuery : true,
28134 displayField : 'value',
28137 forceSelection : true,
28139 placeholder : this.dayPlaceholder,
28140 selectOnFocus : true,
28141 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28142 triggerAction : 'all',
28144 valueField : 'value',
28145 store : new Roo.data.SimpleStore({
28146 data : (function() {
28148 _this.fireEvent('days', _this, days);
28151 fields : [ 'value' ]
28154 select : function (_self, record, index)
28156 _this.setValue(_this.getValue());
28161 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28163 this.monthField = new Roo.bootstrap.MonthField({
28164 after : '<i class=\"fa fa-calendar\"></i>',
28165 allowBlank : this.monthAllowBlank,
28166 placeholder : this.monthPlaceholder,
28169 render : function (_self)
28171 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28172 e.preventDefault();
28176 select : function (_self, oldvalue, newvalue)
28178 _this.setValue(_this.getValue());
28183 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28185 this.yearField = new Roo.bootstrap.ComboBox({
28186 allowBlank : this.yearAllowBlank,
28187 alwaysQuery : true,
28188 displayField : 'value',
28191 forceSelection : true,
28193 placeholder : this.yearPlaceholder,
28194 selectOnFocus : true,
28195 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28196 triggerAction : 'all',
28198 valueField : 'value',
28199 store : new Roo.data.SimpleStore({
28200 data : (function() {
28202 _this.fireEvent('years', _this, years);
28205 fields : [ 'value' ]
28208 select : function (_self, record, index)
28210 _this.setValue(_this.getValue());
28215 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28218 setValue : function(v, format)
28220 this.inputEl.dom.value = v;
28222 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28224 var d = Date.parseDate(v, f);
28231 this.setDay(d.format(this.dayFormat));
28232 this.setMonth(d.format(this.monthFormat));
28233 this.setYear(d.format(this.yearFormat));
28240 setDay : function(v)
28242 this.dayField.setValue(v);
28243 this.inputEl.dom.value = this.getValue();
28248 setMonth : function(v)
28250 this.monthField.setValue(v, true);
28251 this.inputEl.dom.value = this.getValue();
28256 setYear : function(v)
28258 this.yearField.setValue(v);
28259 this.inputEl.dom.value = this.getValue();
28264 getDay : function()
28266 return this.dayField.getValue();
28269 getMonth : function()
28271 return this.monthField.getValue();
28274 getYear : function()
28276 return this.yearField.getValue();
28279 getValue : function()
28281 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28283 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28293 this.inputEl.dom.value = '';
28298 validate : function()
28300 var d = this.dayField.validate();
28301 var m = this.monthField.validate();
28302 var y = this.yearField.validate();
28307 (!this.dayAllowBlank && !d) ||
28308 (!this.monthAllowBlank && !m) ||
28309 (!this.yearAllowBlank && !y)
28314 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28323 this.markInvalid();
28328 markValid : function()
28331 var label = this.el.select('label', true).first();
28332 var icon = this.el.select('i.fa-star', true).first();
28338 this.fireEvent('valid', this);
28342 * Mark this field as invalid
28343 * @param {String} msg The validation message
28345 markInvalid : function(msg)
28348 var label = this.el.select('label', true).first();
28349 var icon = this.el.select('i.fa-star', true).first();
28351 if(label && !icon){
28352 this.el.select('.roo-date-split-field-label', true).createChild({
28354 cls : 'text-danger fa fa-lg fa-star',
28355 tooltip : 'This field is required',
28356 style : 'margin-right:5px;'
28360 this.fireEvent('invalid', this, msg);
28363 clearInvalid : function()
28365 var label = this.el.select('label', true).first();
28366 var icon = this.el.select('i.fa-star', true).first();
28372 this.fireEvent('valid', this);
28375 getName: function()
28385 * http://masonry.desandro.com
28387 * The idea is to render all the bricks based on vertical width...
28389 * The original code extends 'outlayer' - we might need to use that....
28395 * @class Roo.bootstrap.LayoutMasonry
28396 * @extends Roo.bootstrap.Component
28397 * Bootstrap Layout Masonry class
28400 * Create a new Element
28401 * @param {Object} config The config object
28404 Roo.bootstrap.LayoutMasonry = function(config){
28405 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28411 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28414 * @cfg {Boolean} isLayoutInstant = no animation?
28416 isLayoutInstant : false, // needed?
28419 * @cfg {Number} boxWidth width of the columns
28424 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28429 * @cfg {Number} padWidth padding below box..
28434 * @cfg {Number} gutter gutter width..
28439 * @cfg {Boolean} isAutoInitial defalut true
28441 isAutoInitial : true,
28446 * @cfg {Boolean} isHorizontal defalut false
28448 isHorizontal : false,
28450 currentSize : null,
28456 bricks: null, //CompositeElement
28460 _isLayoutInited : false,
28462 // isAlternative : false, // only use for vertical layout...
28465 * @cfg {Number} alternativePadWidth padding below box..
28467 alternativePadWidth : 50,
28469 getAutoCreate : function(){
28473 cls: 'blog-masonary-wrapper ' + this.cls,
28475 cls : 'mas-boxes masonary'
28482 getChildContainer: function( )
28484 if (this.boxesEl) {
28485 return this.boxesEl;
28488 this.boxesEl = this.el.select('.mas-boxes').first();
28490 return this.boxesEl;
28494 initEvents : function()
28498 if(this.isAutoInitial){
28499 Roo.log('hook children rendered');
28500 this.on('childrenrendered', function() {
28501 Roo.log('children rendered');
28507 initial : function()
28509 this.currentSize = this.el.getBox(true);
28511 Roo.EventManager.onWindowResize(this.resize, this);
28513 if(!this.isAutoInitial){
28521 //this.layout.defer(500,this);
28525 resize : function()
28529 var cs = this.el.getBox(true);
28531 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28532 Roo.log("no change in with or X");
28536 this.currentSize = cs;
28542 layout : function()
28544 this._resetLayout();
28546 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28548 this.layoutItems( isInstant );
28550 this._isLayoutInited = true;
28554 _resetLayout : function()
28556 if(this.isHorizontal){
28557 this.horizontalMeasureColumns();
28561 this.verticalMeasureColumns();
28565 verticalMeasureColumns : function()
28567 this.getContainerWidth();
28569 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28570 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28574 var boxWidth = this.boxWidth + this.padWidth;
28576 if(this.containerWidth < this.boxWidth){
28577 boxWidth = this.containerWidth
28580 var containerWidth = this.containerWidth;
28582 var cols = Math.floor(containerWidth / boxWidth);
28584 this.cols = Math.max( cols, 1 );
28586 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28588 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28590 this.colWidth = boxWidth + avail - this.padWidth;
28592 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28593 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28596 horizontalMeasureColumns : function()
28598 this.getContainerWidth();
28600 var boxWidth = this.boxWidth;
28602 if(this.containerWidth < boxWidth){
28603 boxWidth = this.containerWidth;
28606 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28608 this.el.setHeight(boxWidth);
28612 getContainerWidth : function()
28614 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28617 layoutItems : function( isInstant )
28619 var items = Roo.apply([], this.bricks);
28621 if(this.isHorizontal){
28622 this._horizontalLayoutItems( items , isInstant );
28626 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28627 // this._verticalAlternativeLayoutItems( items , isInstant );
28631 this._verticalLayoutItems( items , isInstant );
28635 _verticalLayoutItems : function ( items , isInstant)
28637 if ( !items || !items.length ) {
28642 ['xs', 'xs', 'xs', 'tall'],
28643 ['xs', 'xs', 'tall'],
28644 ['xs', 'xs', 'sm'],
28645 ['xs', 'xs', 'xs'],
28651 ['sm', 'xs', 'xs'],
28655 ['tall', 'xs', 'xs', 'xs'],
28656 ['tall', 'xs', 'xs'],
28668 Roo.each(items, function(item, k){
28670 switch (item.size) {
28671 // these layouts take up a full box,
28682 boxes.push([item]);
28705 var filterPattern = function(box, length)
28713 var pattern = box.slice(0, length);
28717 Roo.each(pattern, function(i){
28718 format.push(i.size);
28721 Roo.each(standard, function(s){
28723 if(String(s) != String(format)){
28732 if(!match && length == 1){
28737 filterPattern(box, length - 1);
28741 queue.push(pattern);
28743 box = box.slice(length, box.length);
28745 filterPattern(box, 4);
28751 Roo.each(boxes, function(box, k){
28757 if(box.length == 1){
28762 filterPattern(box, 4);
28766 this._processVerticalLayoutQueue( queue, isInstant );
28770 // _verticalAlternativeLayoutItems : function( items , isInstant )
28772 // if ( !items || !items.length ) {
28776 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28780 _horizontalLayoutItems : function ( items , isInstant)
28782 if ( !items || !items.length || items.length < 3) {
28788 var eItems = items.slice(0, 3);
28790 items = items.slice(3, items.length);
28793 ['xs', 'xs', 'xs', 'wide'],
28794 ['xs', 'xs', 'wide'],
28795 ['xs', 'xs', 'sm'],
28796 ['xs', 'xs', 'xs'],
28802 ['sm', 'xs', 'xs'],
28806 ['wide', 'xs', 'xs', 'xs'],
28807 ['wide', 'xs', 'xs'],
28820 Roo.each(items, function(item, k){
28822 switch (item.size) {
28833 boxes.push([item]);
28857 var filterPattern = function(box, length)
28865 var pattern = box.slice(0, length);
28869 Roo.each(pattern, function(i){
28870 format.push(i.size);
28873 Roo.each(standard, function(s){
28875 if(String(s) != String(format)){
28884 if(!match && length == 1){
28889 filterPattern(box, length - 1);
28893 queue.push(pattern);
28895 box = box.slice(length, box.length);
28897 filterPattern(box, 4);
28903 Roo.each(boxes, function(box, k){
28909 if(box.length == 1){
28914 filterPattern(box, 4);
28921 var pos = this.el.getBox(true);
28925 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
28927 var hit_end = false;
28929 Roo.each(queue, function(box){
28933 Roo.each(box, function(b){
28935 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28945 Roo.each(box, function(b){
28947 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28950 mx = Math.max(mx, b.x);
28954 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
28958 Roo.each(box, function(b){
28960 b.el.setVisibilityMode(Roo.Element.DISPLAY);
28974 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
28977 /** Sets position of item in DOM
28978 * @param {Element} item
28979 * @param {Number} x - horizontal position
28980 * @param {Number} y - vertical position
28981 * @param {Boolean} isInstant - disables transitions
28983 _processVerticalLayoutQueue : function( queue, isInstant )
28985 var pos = this.el.getBox(true);
28990 for (var i = 0; i < this.cols; i++){
28994 Roo.each(queue, function(box, k){
28996 var col = k % this.cols;
28998 Roo.each(box, function(b,kk){
29000 b.el.position('absolute');
29002 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29003 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29005 if(b.size == 'md-left' || b.size == 'md-right'){
29006 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29007 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29010 b.el.setWidth(width);
29011 b.el.setHeight(height);
29015 for (var i = 0; i < this.cols; i++){
29017 if(maxY[i] < maxY[col]){
29022 col = Math.min(col, i);
29026 x = pos.x + col * (this.colWidth + this.padWidth);
29030 var positions = [];
29032 switch (box.length){
29034 positions = this.getVerticalOneBoxColPositions(x, y, box);
29037 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29040 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29043 positions = this.getVerticalFourBoxColPositions(x, y, box);
29049 Roo.each(box, function(b,kk){
29051 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29053 var sz = b.el.getSize();
29055 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29063 for (var i = 0; i < this.cols; i++){
29064 mY = Math.max(mY, maxY[i]);
29067 this.el.setHeight(mY - pos.y);
29071 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29073 // var pos = this.el.getBox(true);
29076 // var maxX = pos.right;
29078 // var maxHeight = 0;
29080 // Roo.each(items, function(item, k){
29084 // item.el.position('absolute');
29086 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29088 // item.el.setWidth(width);
29090 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29092 // item.el.setHeight(height);
29095 // item.el.setXY([x, y], isInstant ? false : true);
29097 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29100 // y = y + height + this.alternativePadWidth;
29102 // maxHeight = maxHeight + height + this.alternativePadWidth;
29106 // this.el.setHeight(maxHeight);
29110 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29112 var pos = this.el.getBox(true);
29117 var maxX = pos.right;
29119 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29121 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29123 Roo.each(queue, function(box, k){
29125 Roo.each(box, function(b, kk){
29127 b.el.position('absolute');
29129 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29130 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29132 if(b.size == 'md-left' || b.size == 'md-right'){
29133 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29134 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29137 b.el.setWidth(width);
29138 b.el.setHeight(height);
29146 var positions = [];
29148 switch (box.length){
29150 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29153 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29156 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29159 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29165 Roo.each(box, function(b,kk){
29167 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29169 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29177 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29179 Roo.each(eItems, function(b,k){
29181 b.size = (k == 0) ? 'sm' : 'xs';
29182 b.x = (k == 0) ? 2 : 1;
29183 b.y = (k == 0) ? 2 : 1;
29185 b.el.position('absolute');
29187 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29189 b.el.setWidth(width);
29191 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29193 b.el.setHeight(height);
29197 var positions = [];
29200 x : maxX - this.unitWidth * 2 - this.gutter,
29205 x : maxX - this.unitWidth,
29206 y : minY + (this.unitWidth + this.gutter) * 2
29210 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29214 Roo.each(eItems, function(b,k){
29216 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29222 getVerticalOneBoxColPositions : function(x, y, box)
29226 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29228 if(box[0].size == 'md-left'){
29232 if(box[0].size == 'md-right'){
29237 x : x + (this.unitWidth + this.gutter) * rand,
29244 getVerticalTwoBoxColPositions : function(x, y, box)
29248 if(box[0].size == 'xs'){
29252 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29256 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29270 x : x + (this.unitWidth + this.gutter) * 2,
29271 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29278 getVerticalThreeBoxColPositions : function(x, y, box)
29282 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29290 x : x + (this.unitWidth + this.gutter) * 1,
29295 x : x + (this.unitWidth + this.gutter) * 2,
29303 if(box[0].size == 'xs' && box[1].size == 'xs'){
29312 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29316 x : x + (this.unitWidth + this.gutter) * 1,
29330 x : x + (this.unitWidth + this.gutter) * 2,
29335 x : x + (this.unitWidth + this.gutter) * 2,
29336 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29343 getVerticalFourBoxColPositions : function(x, y, box)
29347 if(box[0].size == 'xs'){
29356 y : y + (this.unitHeight + this.gutter) * 1
29361 y : y + (this.unitHeight + this.gutter) * 2
29365 x : x + (this.unitWidth + this.gutter) * 1,
29379 x : x + (this.unitWidth + this.gutter) * 2,
29384 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29385 y : y + (this.unitHeight + this.gutter) * 1
29389 x : x + (this.unitWidth + this.gutter) * 2,
29390 y : y + (this.unitWidth + this.gutter) * 2
29397 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29401 if(box[0].size == 'md-left'){
29403 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29410 if(box[0].size == 'md-right'){
29412 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29413 y : minY + (this.unitWidth + this.gutter) * 1
29419 var rand = Math.floor(Math.random() * (4 - box[0].y));
29422 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29423 y : minY + (this.unitWidth + this.gutter) * rand
29430 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29434 if(box[0].size == 'xs'){
29437 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29442 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29443 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29451 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29456 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29457 y : minY + (this.unitWidth + this.gutter) * 2
29464 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29468 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29471 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29476 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29477 y : minY + (this.unitWidth + this.gutter) * 1
29481 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29482 y : minY + (this.unitWidth + this.gutter) * 2
29489 if(box[0].size == 'xs' && box[1].size == 'xs'){
29492 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29497 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29502 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29503 y : minY + (this.unitWidth + this.gutter) * 1
29511 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29516 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29517 y : minY + (this.unitWidth + this.gutter) * 2
29521 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29522 y : minY + (this.unitWidth + this.gutter) * 2
29529 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29533 if(box[0].size == 'xs'){
29536 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29541 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29546 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),
29551 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29552 y : minY + (this.unitWidth + this.gutter) * 1
29560 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29565 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29566 y : minY + (this.unitWidth + this.gutter) * 2
29570 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29571 y : minY + (this.unitWidth + this.gutter) * 2
29575 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),
29576 y : minY + (this.unitWidth + this.gutter) * 2
29590 * http://masonry.desandro.com
29592 * The idea is to render all the bricks based on vertical width...
29594 * The original code extends 'outlayer' - we might need to use that....
29600 * @class Roo.bootstrap.LayoutMasonryAuto
29601 * @extends Roo.bootstrap.Component
29602 * Bootstrap Layout Masonry class
29605 * Create a new Element
29606 * @param {Object} config The config object
29609 Roo.bootstrap.LayoutMasonryAuto = function(config){
29610 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29613 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29616 * @cfg {Boolean} isFitWidth - resize the width..
29618 isFitWidth : false, // options..
29620 * @cfg {Boolean} isOriginLeft = left align?
29622 isOriginLeft : true,
29624 * @cfg {Boolean} isOriginTop = top align?
29626 isOriginTop : false,
29628 * @cfg {Boolean} isLayoutInstant = no animation?
29630 isLayoutInstant : false, // needed?
29632 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29634 isResizingContainer : true,
29636 * @cfg {Number} columnWidth width of the columns
29641 * @cfg {Number} padHeight padding below box..
29647 * @cfg {Boolean} isAutoInitial defalut true
29650 isAutoInitial : true,
29656 initialColumnWidth : 0,
29657 currentSize : null,
29659 colYs : null, // array.
29666 bricks: null, //CompositeElement
29667 cols : 0, // array?
29668 // element : null, // wrapped now this.el
29669 _isLayoutInited : null,
29672 getAutoCreate : function(){
29676 cls: 'blog-masonary-wrapper ' + this.cls,
29678 cls : 'mas-boxes masonary'
29685 getChildContainer: function( )
29687 if (this.boxesEl) {
29688 return this.boxesEl;
29691 this.boxesEl = this.el.select('.mas-boxes').first();
29693 return this.boxesEl;
29697 initEvents : function()
29701 if(this.isAutoInitial){
29702 Roo.log('hook children rendered');
29703 this.on('childrenrendered', function() {
29704 Roo.log('children rendered');
29711 initial : function()
29713 this.reloadItems();
29715 this.currentSize = this.el.getBox(true);
29717 /// was window resize... - let's see if this works..
29718 Roo.EventManager.onWindowResize(this.resize, this);
29720 if(!this.isAutoInitial){
29725 this.layout.defer(500,this);
29728 reloadItems: function()
29730 this.bricks = this.el.select('.masonry-brick', true);
29732 this.bricks.each(function(b) {
29733 //Roo.log(b.getSize());
29734 if (!b.attr('originalwidth')) {
29735 b.attr('originalwidth', b.getSize().width);
29740 Roo.log(this.bricks.elements.length);
29743 resize : function()
29746 var cs = this.el.getBox(true);
29748 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29749 Roo.log("no change in with or X");
29752 this.currentSize = cs;
29756 layout : function()
29759 this._resetLayout();
29760 //this._manageStamps();
29762 // don't animate first layout
29763 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29764 this.layoutItems( isInstant );
29766 // flag for initalized
29767 this._isLayoutInited = true;
29770 layoutItems : function( isInstant )
29772 //var items = this._getItemsForLayout( this.items );
29773 // original code supports filtering layout items.. we just ignore it..
29775 this._layoutItems( this.bricks , isInstant );
29777 this._postLayout();
29779 _layoutItems : function ( items , isInstant)
29781 //this.fireEvent( 'layout', this, items );
29784 if ( !items || !items.elements.length ) {
29785 // no items, emit event with empty array
29790 items.each(function(item) {
29791 Roo.log("layout item");
29793 // get x/y object from method
29794 var position = this._getItemLayoutPosition( item );
29796 position.item = item;
29797 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29798 queue.push( position );
29801 this._processLayoutQueue( queue );
29803 /** Sets position of item in DOM
29804 * @param {Element} item
29805 * @param {Number} x - horizontal position
29806 * @param {Number} y - vertical position
29807 * @param {Boolean} isInstant - disables transitions
29809 _processLayoutQueue : function( queue )
29811 for ( var i=0, len = queue.length; i < len; i++ ) {
29812 var obj = queue[i];
29813 obj.item.position('absolute');
29814 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29820 * Any logic you want to do after each layout,
29821 * i.e. size the container
29823 _postLayout : function()
29825 this.resizeContainer();
29828 resizeContainer : function()
29830 if ( !this.isResizingContainer ) {
29833 var size = this._getContainerSize();
29835 this.el.setSize(size.width,size.height);
29836 this.boxesEl.setSize(size.width,size.height);
29842 _resetLayout : function()
29844 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29845 this.colWidth = this.el.getWidth();
29846 //this.gutter = this.el.getWidth();
29848 this.measureColumns();
29854 this.colYs.push( 0 );
29860 measureColumns : function()
29862 this.getContainerWidth();
29863 // if columnWidth is 0, default to outerWidth of first item
29864 if ( !this.columnWidth ) {
29865 var firstItem = this.bricks.first();
29866 Roo.log(firstItem);
29867 this.columnWidth = this.containerWidth;
29868 if (firstItem && firstItem.attr('originalwidth') ) {
29869 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29871 // columnWidth fall back to item of first element
29872 Roo.log("set column width?");
29873 this.initialColumnWidth = this.columnWidth ;
29875 // if first elem has no width, default to size of container
29880 if (this.initialColumnWidth) {
29881 this.columnWidth = this.initialColumnWidth;
29886 // column width is fixed at the top - however if container width get's smaller we should
29889 // this bit calcs how man columns..
29891 var columnWidth = this.columnWidth += this.gutter;
29893 // calculate columns
29894 var containerWidth = this.containerWidth + this.gutter;
29896 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
29897 // fix rounding errors, typically with gutters
29898 var excess = columnWidth - containerWidth % columnWidth;
29901 // if overshoot is less than a pixel, round up, otherwise floor it
29902 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
29903 cols = Math[ mathMethod ]( cols );
29904 this.cols = Math.max( cols, 1 );
29907 // padding positioning..
29908 var totalColWidth = this.cols * this.columnWidth;
29909 var padavail = this.containerWidth - totalColWidth;
29910 // so for 2 columns - we need 3 'pads'
29912 var padNeeded = (1+this.cols) * this.padWidth;
29914 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
29916 this.columnWidth += padExtra
29917 //this.padWidth = Math.floor(padavail / ( this.cols));
29919 // adjust colum width so that padding is fixed??
29921 // we have 3 columns ... total = width * 3
29922 // we have X left over... that should be used by
29924 //if (this.expandC) {
29932 getContainerWidth : function()
29934 /* // container is parent if fit width
29935 var container = this.isFitWidth ? this.element.parentNode : this.element;
29936 // check that this.size and size are there
29937 // IE8 triggers resize on body size change, so they might not be
29939 var size = getSize( container ); //FIXME
29940 this.containerWidth = size && size.innerWidth; //FIXME
29943 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29947 _getItemLayoutPosition : function( item ) // what is item?
29949 // we resize the item to our columnWidth..
29951 item.setWidth(this.columnWidth);
29952 item.autoBoxAdjust = false;
29954 var sz = item.getSize();
29956 // how many columns does this brick span
29957 var remainder = this.containerWidth % this.columnWidth;
29959 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
29960 // round if off by 1 pixel, otherwise use ceil
29961 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
29962 colSpan = Math.min( colSpan, this.cols );
29964 // normally this should be '1' as we dont' currently allow multi width columns..
29966 var colGroup = this._getColGroup( colSpan );
29967 // get the minimum Y value from the columns
29968 var minimumY = Math.min.apply( Math, colGroup );
29969 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
29971 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
29973 // position the brick
29975 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
29976 y: this.currentSize.y + minimumY + this.padHeight
29980 // apply setHeight to necessary columns
29981 var setHeight = minimumY + sz.height + this.padHeight;
29982 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
29984 var setSpan = this.cols + 1 - colGroup.length;
29985 for ( var i = 0; i < setSpan; i++ ) {
29986 this.colYs[ shortColIndex + i ] = setHeight ;
29993 * @param {Number} colSpan - number of columns the element spans
29994 * @returns {Array} colGroup
29996 _getColGroup : function( colSpan )
29998 if ( colSpan < 2 ) {
29999 // if brick spans only one column, use all the column Ys
30004 // how many different places could this brick fit horizontally
30005 var groupCount = this.cols + 1 - colSpan;
30006 // for each group potential horizontal position
30007 for ( var i = 0; i < groupCount; i++ ) {
30008 // make an array of colY values for that one group
30009 var groupColYs = this.colYs.slice( i, i + colSpan );
30010 // and get the max value of the array
30011 colGroup[i] = Math.max.apply( Math, groupColYs );
30016 _manageStamp : function( stamp )
30018 var stampSize = stamp.getSize();
30019 var offset = stamp.getBox();
30020 // get the columns that this stamp affects
30021 var firstX = this.isOriginLeft ? offset.x : offset.right;
30022 var lastX = firstX + stampSize.width;
30023 var firstCol = Math.floor( firstX / this.columnWidth );
30024 firstCol = Math.max( 0, firstCol );
30026 var lastCol = Math.floor( lastX / this.columnWidth );
30027 // lastCol should not go over if multiple of columnWidth #425
30028 lastCol -= lastX % this.columnWidth ? 0 : 1;
30029 lastCol = Math.min( this.cols - 1, lastCol );
30031 // set colYs to bottom of the stamp
30032 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30035 for ( var i = firstCol; i <= lastCol; i++ ) {
30036 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30041 _getContainerSize : function()
30043 this.maxY = Math.max.apply( Math, this.colYs );
30048 if ( this.isFitWidth ) {
30049 size.width = this._getContainerFitWidth();
30055 _getContainerFitWidth : function()
30057 var unusedCols = 0;
30058 // count unused columns
30061 if ( this.colYs[i] !== 0 ) {
30066 // fit container to columns that have been used
30067 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30070 needsResizeLayout : function()
30072 var previousWidth = this.containerWidth;
30073 this.getContainerWidth();
30074 return previousWidth !== this.containerWidth;
30089 * @class Roo.bootstrap.MasonryBrick
30090 * @extends Roo.bootstrap.Component
30091 * Bootstrap MasonryBrick class
30094 * Create a new MasonryBrick
30095 * @param {Object} config The config object
30098 Roo.bootstrap.MasonryBrick = function(config){
30099 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30105 * When a MasonryBrick is clcik
30106 * @param {Roo.bootstrap.MasonryBrick} this
30107 * @param {Roo.EventObject} e
30113 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30116 * @cfg {String} title
30120 * @cfg {String} html
30124 * @cfg {String} bgimage
30128 * @cfg {String} cls
30132 * @cfg {String} href
30136 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30141 * @cfg {String} (center|bottom) placetitle
30145 getAutoCreate : function()
30147 var cls = 'masonry-brick';
30149 if(this.href.length){
30150 cls += ' masonry-brick-link';
30153 if(this.bgimage.length){
30154 cls += ' masonry-brick-image';
30158 cls += ' masonry-' + this.size + '-brick';
30161 if(this.placetitle.length){
30163 switch (this.placetitle) {
30165 cls += ' masonry-center-title';
30168 cls += ' masonry-bottom-title';
30175 if(!this.html.length && !this.bgimage.length){
30176 cls += ' masonry-center-title';
30179 if(!this.html.length && this.bgimage.length){
30180 cls += ' masonry-bottom-title';
30185 cls += ' ' + this.cls;
30189 tag: (this.href.length) ? 'a' : 'div',
30194 cls: 'masonry-brick-paragraph',
30200 if(this.href.length){
30201 cfg.href = this.href;
30204 var cn = cfg.cn[0].cn;
30206 if(this.title.length){
30209 cls: 'masonry-brick-title',
30214 if(this.html.length){
30217 cls: 'masonry-brick-text',
30222 if(this.bgimage.length){
30225 cls: 'masonry-brick-image-view',
30234 initEvents: function()
30236 switch (this.size) {
30238 // this.intSize = 1;
30243 // this.intSize = 2;
30250 // this.intSize = 3;
30255 // this.intSize = 3;
30260 // this.intSize = 3;
30265 // this.intSize = 3;
30277 this.el.on('touchstart', this.onTouchStart, this);
30278 this.el.on('touchmove', this.onTouchMove, this);
30279 this.el.on('touchend', this.onTouchEnd, this);
30281 this.el.on('mouseenter' ,this.enter, this);
30282 this.el.on('mouseleave', this.leave, this);
30285 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30286 this.parent().bricks.push(this);
30291 onClick: function(e, el)
30299 var time = this.endTimer - this.startTimer;
30307 e.preventDefault();
30310 enter: function(e, el)
30312 e.preventDefault();
30314 if(this.bgimage.length && this.html.length){
30315 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30319 leave: function(e, el)
30321 e.preventDefault();
30323 if(this.bgimage.length && this.html.length){
30324 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30328 onTouchStart: function(e, el)
30330 // e.preventDefault();
30332 if(!this.bgimage.length || !this.html.length){
30336 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30338 this.timer = new Date().getTime();
30340 this.touchmoved = false;
30343 onTouchMove: function(e, el)
30345 this.touchmoved = true;
30348 onTouchEnd: function(e, el)
30350 // e.preventDefault();
30352 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30356 if(!this.bgimage.length || !this.html.length){
30358 if(this.href.length){
30359 window.location.href = this.href;
30365 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30367 window.location.href = this.href;
30382 * @class Roo.bootstrap.Brick
30383 * @extends Roo.bootstrap.Component
30384 * Bootstrap Brick class
30387 * Create a new Brick
30388 * @param {Object} config The config object
30391 Roo.bootstrap.Brick = function(config){
30392 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30398 * When a Brick is click
30399 * @param {Roo.bootstrap.Brick} this
30400 * @param {Roo.EventObject} e
30406 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30409 * @cfg {String} title
30413 * @cfg {String} html
30417 * @cfg {String} bgimage
30421 * @cfg {String} cls
30425 * @cfg {String} href
30429 * @cfg {String} video
30433 * @cfg {Boolean} square
30437 getAutoCreate : function()
30439 var cls = 'roo-brick';
30441 if(this.href.length){
30442 cls += ' roo-brick-link';
30445 if(this.bgimage.length){
30446 cls += ' roo-brick-image';
30449 if(!this.html.length && !this.bgimage.length){
30450 cls += ' roo-brick-center-title';
30453 if(!this.html.length && this.bgimage.length){
30454 cls += ' roo-brick-bottom-title';
30458 cls += ' ' + this.cls;
30462 tag: (this.href.length) ? 'a' : 'div',
30467 cls: 'roo-brick-paragraph',
30473 if(this.href.length){
30474 cfg.href = this.href;
30477 var cn = cfg.cn[0].cn;
30479 if(this.title.length){
30482 cls: 'roo-brick-title',
30487 if(this.html.length){
30490 cls: 'roo-brick-text',
30495 if(this.bgimage.length){
30498 cls: 'roo-brick-image-view',
30506 initEvents: function()
30508 if(this.title.length || this.html.length){
30509 this.el.on('mouseenter' ,this.enter, this);
30510 this.el.on('mouseleave', this.leave, this);
30514 Roo.EventManager.onWindowResize(this.resize, this);
30519 resize : function()
30521 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30523 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30524 // paragraph.setHeight(paragraph.getWidth());
30526 if(this.bgimage.length){
30527 var image = this.el.select('.roo-brick-image-view', true).first();
30528 image.setWidth(paragraph.getWidth());
30529 image.setHeight(paragraph.getWidth());
30534 enter: function(e, el)
30536 e.preventDefault();
30538 if(this.bgimage.length){
30539 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30540 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30544 leave: function(e, el)
30546 e.preventDefault();
30548 if(this.bgimage.length){
30549 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30550 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30560 * Ext JS Library 1.1.1
30561 * Copyright(c) 2006-2007, Ext JS, LLC.
30563 * Originally Released Under LGPL - original licence link has changed is not relivant.
30566 * <script type="text/javascript">
30571 * @class Roo.bootstrap.SplitBar
30572 * @extends Roo.util.Observable
30573 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30577 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30578 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30579 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30580 split.minSize = 100;
30581 split.maxSize = 600;
30582 split.animate = true;
30583 split.on('moved', splitterMoved);
30586 * Create a new SplitBar
30587 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30588 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30589 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30590 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30591 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30592 position of the SplitBar).
30594 Roo.bootstrap.SplitBar = function(cfg){
30599 // dragElement : elm
30600 // resizingElement: el,
30602 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30603 // placement : Roo.bootstrap.SplitBar.LEFT ,
30604 // existingProxy ???
30607 this.el = Roo.get(cfg.dragElement, true);
30608 this.el.dom.unselectable = "on";
30610 this.resizingEl = Roo.get(cfg.resizingElement, true);
30614 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30615 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30618 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30621 * The minimum size of the resizing element. (Defaults to 0)
30627 * The maximum size of the resizing element. (Defaults to 2000)
30630 this.maxSize = 2000;
30633 * Whether to animate the transition to the new size
30636 this.animate = false;
30639 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30642 this.useShim = false;
30647 if(!cfg.existingProxy){
30649 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30651 this.proxy = Roo.get(cfg.existingProxy).dom;
30654 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30657 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30660 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30663 this.dragSpecs = {};
30666 * @private The adapter to use to positon and resize elements
30668 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30669 this.adapter.init(this);
30671 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30673 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30674 this.el.addClass("roo-splitbar-h");
30677 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30678 this.el.addClass("roo-splitbar-v");
30684 * Fires when the splitter is moved (alias for {@link #event-moved})
30685 * @param {Roo.bootstrap.SplitBar} this
30686 * @param {Number} newSize the new width or height
30691 * Fires when the splitter is moved
30692 * @param {Roo.bootstrap.SplitBar} this
30693 * @param {Number} newSize the new width or height
30697 * @event beforeresize
30698 * Fires before the splitter is dragged
30699 * @param {Roo.bootstrap.SplitBar} this
30701 "beforeresize" : true,
30703 "beforeapply" : true
30706 Roo.util.Observable.call(this);
30709 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30710 onStartProxyDrag : function(x, y){
30711 this.fireEvent("beforeresize", this);
30713 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30715 o.enableDisplayMode("block");
30716 // all splitbars share the same overlay
30717 Roo.bootstrap.SplitBar.prototype.overlay = o;
30719 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30720 this.overlay.show();
30721 Roo.get(this.proxy).setDisplayed("block");
30722 var size = this.adapter.getElementSize(this);
30723 this.activeMinSize = this.getMinimumSize();;
30724 this.activeMaxSize = this.getMaximumSize();;
30725 var c1 = size - this.activeMinSize;
30726 var c2 = Math.max(this.activeMaxSize - size, 0);
30727 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30728 this.dd.resetConstraints();
30729 this.dd.setXConstraint(
30730 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30731 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30733 this.dd.setYConstraint(0, 0);
30735 this.dd.resetConstraints();
30736 this.dd.setXConstraint(0, 0);
30737 this.dd.setYConstraint(
30738 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30739 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30742 this.dragSpecs.startSize = size;
30743 this.dragSpecs.startPoint = [x, y];
30744 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30748 * @private Called after the drag operation by the DDProxy
30750 onEndProxyDrag : function(e){
30751 Roo.get(this.proxy).setDisplayed(false);
30752 var endPoint = Roo.lib.Event.getXY(e);
30754 this.overlay.hide();
30757 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30758 newSize = this.dragSpecs.startSize +
30759 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30760 endPoint[0] - this.dragSpecs.startPoint[0] :
30761 this.dragSpecs.startPoint[0] - endPoint[0]
30764 newSize = this.dragSpecs.startSize +
30765 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30766 endPoint[1] - this.dragSpecs.startPoint[1] :
30767 this.dragSpecs.startPoint[1] - endPoint[1]
30770 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30771 if(newSize != this.dragSpecs.startSize){
30772 if(this.fireEvent('beforeapply', this, newSize) !== false){
30773 this.adapter.setElementSize(this, newSize);
30774 this.fireEvent("moved", this, newSize);
30775 this.fireEvent("resize", this, newSize);
30781 * Get the adapter this SplitBar uses
30782 * @return The adapter object
30784 getAdapter : function(){
30785 return this.adapter;
30789 * Set the adapter this SplitBar uses
30790 * @param {Object} adapter A SplitBar adapter object
30792 setAdapter : function(adapter){
30793 this.adapter = adapter;
30794 this.adapter.init(this);
30798 * Gets the minimum size for the resizing element
30799 * @return {Number} The minimum size
30801 getMinimumSize : function(){
30802 return this.minSize;
30806 * Sets the minimum size for the resizing element
30807 * @param {Number} minSize The minimum size
30809 setMinimumSize : function(minSize){
30810 this.minSize = minSize;
30814 * Gets the maximum size for the resizing element
30815 * @return {Number} The maximum size
30817 getMaximumSize : function(){
30818 return this.maxSize;
30822 * Sets the maximum size for the resizing element
30823 * @param {Number} maxSize The maximum size
30825 setMaximumSize : function(maxSize){
30826 this.maxSize = maxSize;
30830 * Sets the initialize size for the resizing element
30831 * @param {Number} size The initial size
30833 setCurrentSize : function(size){
30834 var oldAnimate = this.animate;
30835 this.animate = false;
30836 this.adapter.setElementSize(this, size);
30837 this.animate = oldAnimate;
30841 * Destroy this splitbar.
30842 * @param {Boolean} removeEl True to remove the element
30844 destroy : function(removeEl){
30846 this.shim.remove();
30849 this.proxy.parentNode.removeChild(this.proxy);
30857 * @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.
30859 Roo.bootstrap.SplitBar.createProxy = function(dir){
30860 var proxy = new Roo.Element(document.createElement("div"));
30861 proxy.unselectable();
30862 var cls = 'roo-splitbar-proxy';
30863 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
30864 document.body.appendChild(proxy.dom);
30869 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
30870 * Default Adapter. It assumes the splitter and resizing element are not positioned
30871 * elements and only gets/sets the width of the element. Generally used for table based layouts.
30873 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
30876 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
30877 // do nothing for now
30878 init : function(s){
30882 * Called before drag operations to get the current size of the resizing element.
30883 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30885 getElementSize : function(s){
30886 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30887 return s.resizingEl.getWidth();
30889 return s.resizingEl.getHeight();
30894 * Called after drag operations to set the size of the resizing element.
30895 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
30896 * @param {Number} newSize The new size to set
30897 * @param {Function} onComplete A function to be invoked when resizing is complete
30899 setElementSize : function(s, newSize, onComplete){
30900 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30902 s.resizingEl.setWidth(newSize);
30904 onComplete(s, newSize);
30907 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
30912 s.resizingEl.setHeight(newSize);
30914 onComplete(s, newSize);
30917 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
30924 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
30925 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
30926 * Adapter that moves the splitter element to align with the resized sizing element.
30927 * Used with an absolute positioned SplitBar.
30928 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
30929 * document.body, make sure you assign an id to the body element.
30931 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
30932 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30933 this.container = Roo.get(container);
30936 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
30937 init : function(s){
30938 this.basic.init(s);
30941 getElementSize : function(s){
30942 return this.basic.getElementSize(s);
30945 setElementSize : function(s, newSize, onComplete){
30946 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
30949 moveSplitter : function(s){
30950 var yes = Roo.bootstrap.SplitBar;
30951 switch(s.placement){
30953 s.el.setX(s.resizingEl.getRight());
30956 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
30959 s.el.setY(s.resizingEl.getBottom());
30962 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
30969 * Orientation constant - Create a vertical SplitBar
30973 Roo.bootstrap.SplitBar.VERTICAL = 1;
30976 * Orientation constant - Create a horizontal SplitBar
30980 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
30983 * Placement constant - The resizing element is to the left of the splitter element
30987 Roo.bootstrap.SplitBar.LEFT = 1;
30990 * Placement constant - The resizing element is to the right of the splitter element
30994 Roo.bootstrap.SplitBar.RIGHT = 2;
30997 * Placement constant - The resizing element is positioned above the splitter element
31001 Roo.bootstrap.SplitBar.TOP = 3;
31004 * Placement constant - The resizing element is positioned under splitter element
31008 Roo.bootstrap.SplitBar.BOTTOM = 4;
31009 Roo.namespace("Roo.bootstrap.layout");/*
31011 * Ext JS Library 1.1.1
31012 * Copyright(c) 2006-2007, Ext JS, LLC.
31014 * Originally Released Under LGPL - original licence link has changed is not relivant.
31017 * <script type="text/javascript">
31021 * @class Roo.bootstrap.layout.Manager
31022 * @extends Roo.bootstrap.Component
31023 * Base class for layout managers.
31025 Roo.bootstrap.layout.Manager = function(config)
31027 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31028 this.el = Roo.get(config.el);
31029 // ie scrollbar fix
31030 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31031 document.body.scroll = "no";
31032 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31033 this.el.position('relative');
31036 this.id = this.el.id;
31037 this.el.addClass("roo-layout-container");
31038 /** false to disable window resize monitoring @type Boolean */
31039 this.monitorWindowResize = true;
31044 * Fires when a layout is performed.
31045 * @param {Roo.LayoutManager} this
31049 * @event regionresized
31050 * Fires when the user resizes a region.
31051 * @param {Roo.LayoutRegion} region The resized region
31052 * @param {Number} newSize The new size (width for east/west, height for north/south)
31054 "regionresized" : true,
31056 * @event regioncollapsed
31057 * Fires when a region is collapsed.
31058 * @param {Roo.LayoutRegion} region The collapsed region
31060 "regioncollapsed" : true,
31062 * @event regionexpanded
31063 * Fires when a region is expanded.
31064 * @param {Roo.LayoutRegion} region The expanded region
31066 "regionexpanded" : true
31068 this.updating = false;
31069 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31072 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31077 monitorWindowResize : true,
31083 * Returns true if this layout is currently being updated
31084 * @return {Boolean}
31086 isUpdating : function(){
31087 return this.updating;
31091 * Suspend the LayoutManager from doing auto-layouts while
31092 * making multiple add or remove calls
31094 beginUpdate : function(){
31095 this.updating = true;
31099 * Restore auto-layouts and optionally disable the manager from performing a layout
31100 * @param {Boolean} noLayout true to disable a layout update
31102 endUpdate : function(noLayout){
31103 this.updating = false;
31109 layout: function(){
31113 onRegionResized : function(region, newSize){
31114 this.fireEvent("regionresized", region, newSize);
31118 onRegionCollapsed : function(region){
31119 this.fireEvent("regioncollapsed", region);
31122 onRegionExpanded : function(region){
31123 this.fireEvent("regionexpanded", region);
31127 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31128 * performs box-model adjustments.
31129 * @return {Object} The size as an object {width: (the width), height: (the height)}
31131 getViewSize : function()
31134 if(this.el.dom != document.body){
31135 size = this.el.getSize();
31137 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31139 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31140 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31145 * Returns the Element this layout is bound to.
31146 * @return {Roo.Element}
31148 getEl : function(){
31153 * Returns the specified region.
31154 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31155 * @return {Roo.LayoutRegion}
31157 getRegion : function(target){
31158 return this.regions[target.toLowerCase()];
31161 onWindowResize : function(){
31162 if(this.monitorWindowResize){
31168 * Ext JS Library 1.1.1
31169 * Copyright(c) 2006-2007, Ext JS, LLC.
31171 * Originally Released Under LGPL - original licence link has changed is not relivant.
31174 * <script type="text/javascript">
31177 * @class Roo.bootstrap.layout.Border
31178 * @extends Roo.bootstrap.layout.Manager
31179 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31180 * please see: examples/bootstrap/nested.html<br><br>
31182 <b>The container the layout is rendered into can be either the body element or any other element.
31183 If it is not the body element, the container needs to either be an absolute positioned element,
31184 or you will need to add "position:relative" to the css of the container. You will also need to specify
31185 the container size if it is not the body element.</b>
31188 * Create a new Border
31189 * @param {Object} config Configuration options
31191 Roo.bootstrap.layout.Border = function(config){
31192 config = config || {};
31193 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31197 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31198 if(config[region]){
31199 config[region].region = region;
31200 this.addRegion(config[region]);
31206 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31208 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31210 * Creates and adds a new region if it doesn't already exist.
31211 * @param {String} target The target region key (north, south, east, west or center).
31212 * @param {Object} config The regions config object
31213 * @return {BorderLayoutRegion} The new region
31215 addRegion : function(config)
31217 if(!this.regions[config.region]){
31218 var r = this.factory(config);
31219 this.bindRegion(r);
31221 return this.regions[config.region];
31225 bindRegion : function(r){
31226 this.regions[r.config.region] = r;
31228 r.on("visibilitychange", this.layout, this);
31229 r.on("paneladded", this.layout, this);
31230 r.on("panelremoved", this.layout, this);
31231 r.on("invalidated", this.layout, this);
31232 r.on("resized", this.onRegionResized, this);
31233 r.on("collapsed", this.onRegionCollapsed, this);
31234 r.on("expanded", this.onRegionExpanded, this);
31238 * Performs a layout update.
31240 layout : function()
31242 if(this.updating) {
31245 var size = this.getViewSize();
31246 var w = size.width;
31247 var h = size.height;
31252 //var x = 0, y = 0;
31254 var rs = this.regions;
31255 var north = rs["north"];
31256 var south = rs["south"];
31257 var west = rs["west"];
31258 var east = rs["east"];
31259 var center = rs["center"];
31260 //if(this.hideOnLayout){ // not supported anymore
31261 //c.el.setStyle("display", "none");
31263 if(north && north.isVisible()){
31264 var b = north.getBox();
31265 var m = north.getMargins();
31266 b.width = w - (m.left+m.right);
31269 centerY = b.height + b.y + m.bottom;
31270 centerH -= centerY;
31271 north.updateBox(this.safeBox(b));
31273 if(south && south.isVisible()){
31274 var b = south.getBox();
31275 var m = south.getMargins();
31276 b.width = w - (m.left+m.right);
31278 var totalHeight = (b.height + m.top + m.bottom);
31279 b.y = h - totalHeight + m.top;
31280 centerH -= totalHeight;
31281 south.updateBox(this.safeBox(b));
31283 if(west && west.isVisible()){
31284 var b = west.getBox();
31285 var m = west.getMargins();
31286 b.height = centerH - (m.top+m.bottom);
31288 b.y = centerY + m.top;
31289 var totalWidth = (b.width + m.left + m.right);
31290 centerX += totalWidth;
31291 centerW -= totalWidth;
31292 west.updateBox(this.safeBox(b));
31294 if(east && east.isVisible()){
31295 var b = east.getBox();
31296 var m = east.getMargins();
31297 b.height = centerH - (m.top+m.bottom);
31298 var totalWidth = (b.width + m.left + m.right);
31299 b.x = w - totalWidth + m.left;
31300 b.y = centerY + m.top;
31301 centerW -= totalWidth;
31302 east.updateBox(this.safeBox(b));
31305 var m = center.getMargins();
31307 x: centerX + m.left,
31308 y: centerY + m.top,
31309 width: centerW - (m.left+m.right),
31310 height: centerH - (m.top+m.bottom)
31312 //if(this.hideOnLayout){
31313 //center.el.setStyle("display", "block");
31315 center.updateBox(this.safeBox(centerBox));
31318 this.fireEvent("layout", this);
31322 safeBox : function(box){
31323 box.width = Math.max(0, box.width);
31324 box.height = Math.max(0, box.height);
31329 * Adds a ContentPanel (or subclass) to this layout.
31330 * @param {String} target The target region key (north, south, east, west or center).
31331 * @param {Roo.ContentPanel} panel The panel to add
31332 * @return {Roo.ContentPanel} The added panel
31334 add : function(target, panel){
31336 target = target.toLowerCase();
31337 return this.regions[target].add(panel);
31341 * Remove a ContentPanel (or subclass) to this layout.
31342 * @param {String} target The target region key (north, south, east, west or center).
31343 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31344 * @return {Roo.ContentPanel} The removed panel
31346 remove : function(target, panel){
31347 target = target.toLowerCase();
31348 return this.regions[target].remove(panel);
31352 * Searches all regions for a panel with the specified id
31353 * @param {String} panelId
31354 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31356 findPanel : function(panelId){
31357 var rs = this.regions;
31358 for(var target in rs){
31359 if(typeof rs[target] != "function"){
31360 var p = rs[target].getPanel(panelId);
31370 * Searches all regions for a panel with the specified id and activates (shows) it.
31371 * @param {String/ContentPanel} panelId The panels id or the panel itself
31372 * @return {Roo.ContentPanel} The shown panel or null
31374 showPanel : function(panelId) {
31375 var rs = this.regions;
31376 for(var target in rs){
31377 var r = rs[target];
31378 if(typeof r != "function"){
31379 if(r.hasPanel(panelId)){
31380 return r.showPanel(panelId);
31388 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31389 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31392 restoreState : function(provider){
31394 provider = Roo.state.Manager;
31396 var sm = new Roo.LayoutStateManager();
31397 sm.init(this, provider);
31403 * Adds a xtype elements to the layout.
31407 xtype : 'ContentPanel',
31414 xtype : 'NestedLayoutPanel',
31420 items : [ ... list of content panels or nested layout panels.. ]
31424 * @param {Object} cfg Xtype definition of item to add.
31426 addxtype : function(cfg)
31428 // basically accepts a pannel...
31429 // can accept a layout region..!?!?
31430 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31433 // theory? children can only be panels??
31435 //if (!cfg.xtype.match(/Panel$/)) {
31440 if (typeof(cfg.region) == 'undefined') {
31441 Roo.log("Failed to add Panel, region was not set");
31445 var region = cfg.region;
31451 xitems = cfg.items;
31458 case 'Content': // ContentPanel (el, cfg)
31459 case 'Scroll': // ContentPanel (el, cfg)
31461 cfg.autoCreate = true;
31462 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31464 // var el = this.el.createChild();
31465 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31468 this.add(region, ret);
31472 case 'TreePanel': // our new panel!
31473 cfg.el = this.el.createChild();
31474 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31475 this.add(region, ret);
31480 // create a new Layout (which is a Border Layout...
31482 var clayout = cfg.layout;
31483 clayout.el = this.el.createChild();
31484 clayout.items = clayout.items || [];
31488 // replace this exitems with the clayout ones..
31489 xitems = clayout.items;
31491 // force background off if it's in center...
31492 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31493 cfg.background = false;
31495 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31498 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31499 //console.log('adding nested layout panel ' + cfg.toSource());
31500 this.add(region, ret);
31501 nb = {}; /// find first...
31506 // needs grid and region
31508 //var el = this.getRegion(region).el.createChild();
31509 var el = this.el.createChild();
31510 // create the grid first...
31511 cfg.grid.container = el;
31512 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31515 if (region == 'center' && this.active ) {
31516 cfg.background = false;
31519 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31521 this.add(region, ret);
31523 if (cfg.background) {
31524 // render grid on panel activation (if panel background)
31525 ret.on('activate', function(gp) {
31526 if (!gp.grid.rendered) {
31527 gp.grid.render(gp.grid.getGridEl());
31531 cfg.grid.render(cfg.grid.getGridEl());
31536 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31537 // it was the old xcomponent building that caused this before.
31538 // espeically if border is the top element in the tree.
31548 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31550 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31551 this.add(region, ret);
31555 throw "Can not add '" + cfg.xtype + "' to Border";
31561 this.beginUpdate();
31565 Roo.each(xitems, function(i) {
31566 region = nb && i.region ? i.region : false;
31568 var add = ret.addxtype(i);
31571 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31572 if (!i.background) {
31573 abn[region] = nb[region] ;
31580 // make the last non-background panel active..
31581 //if (nb) { Roo.log(abn); }
31584 for(var r in abn) {
31585 region = this.getRegion(r);
31587 // tried using nb[r], but it does not work..
31589 region.showPanel(abn[r]);
31600 factory : function(cfg)
31603 var validRegions = Roo.bootstrap.layout.Border.regions;
31605 var target = cfg.region;
31608 var r = Roo.bootstrap.layout;
31612 return new r.North(cfg);
31614 return new r.South(cfg);
31616 return new r.East(cfg);
31618 return new r.West(cfg);
31620 return new r.Center(cfg);
31622 throw 'Layout region "'+target+'" not supported.';
31629 * Ext JS Library 1.1.1
31630 * Copyright(c) 2006-2007, Ext JS, LLC.
31632 * Originally Released Under LGPL - original licence link has changed is not relivant.
31635 * <script type="text/javascript">
31639 * @class Roo.bootstrap.layout.Basic
31640 * @extends Roo.util.Observable
31641 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31642 * and does not have a titlebar, tabs or any other features. All it does is size and position
31643 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31644 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31645 * @cfg {string} region the region that it inhabits..
31646 * @cfg {bool} skipConfig skip config?
31650 Roo.bootstrap.layout.Basic = function(config){
31652 this.mgr = config.mgr;
31654 this.position = config.region;
31656 var skipConfig = config.skipConfig;
31660 * @scope Roo.BasicLayoutRegion
31664 * @event beforeremove
31665 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31666 * @param {Roo.LayoutRegion} this
31667 * @param {Roo.ContentPanel} panel The panel
31668 * @param {Object} e The cancel event object
31670 "beforeremove" : true,
31672 * @event invalidated
31673 * Fires when the layout for this region is changed.
31674 * @param {Roo.LayoutRegion} this
31676 "invalidated" : true,
31678 * @event visibilitychange
31679 * Fires when this region is shown or hidden
31680 * @param {Roo.LayoutRegion} this
31681 * @param {Boolean} visibility true or false
31683 "visibilitychange" : true,
31685 * @event paneladded
31686 * Fires when a panel is added.
31687 * @param {Roo.LayoutRegion} this
31688 * @param {Roo.ContentPanel} panel The panel
31690 "paneladded" : true,
31692 * @event panelremoved
31693 * Fires when a panel is removed.
31694 * @param {Roo.LayoutRegion} this
31695 * @param {Roo.ContentPanel} panel The panel
31697 "panelremoved" : true,
31699 * @event beforecollapse
31700 * Fires when this region before collapse.
31701 * @param {Roo.LayoutRegion} this
31703 "beforecollapse" : true,
31706 * Fires when this region is collapsed.
31707 * @param {Roo.LayoutRegion} this
31709 "collapsed" : true,
31712 * Fires when this region is expanded.
31713 * @param {Roo.LayoutRegion} this
31718 * Fires when this region is slid into view.
31719 * @param {Roo.LayoutRegion} this
31721 "slideshow" : true,
31724 * Fires when this region slides out of view.
31725 * @param {Roo.LayoutRegion} this
31727 "slidehide" : true,
31729 * @event panelactivated
31730 * Fires when a panel is activated.
31731 * @param {Roo.LayoutRegion} this
31732 * @param {Roo.ContentPanel} panel The activated panel
31734 "panelactivated" : true,
31737 * Fires when the user resizes this region.
31738 * @param {Roo.LayoutRegion} this
31739 * @param {Number} newSize The new size (width for east/west, height for north/south)
31743 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31744 this.panels = new Roo.util.MixedCollection();
31745 this.panels.getKey = this.getPanelId.createDelegate(this);
31747 this.activePanel = null;
31748 // ensure listeners are added...
31750 if (config.listeners || config.events) {
31751 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31752 listeners : config.listeners || {},
31753 events : config.events || {}
31757 if(skipConfig !== true){
31758 this.applyConfig(config);
31762 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31764 getPanelId : function(p){
31768 applyConfig : function(config){
31769 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31770 this.config = config;
31775 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31776 * the width, for horizontal (north, south) the height.
31777 * @param {Number} newSize The new width or height
31779 resizeTo : function(newSize){
31780 var el = this.el ? this.el :
31781 (this.activePanel ? this.activePanel.getEl() : null);
31783 switch(this.position){
31786 el.setWidth(newSize);
31787 this.fireEvent("resized", this, newSize);
31791 el.setHeight(newSize);
31792 this.fireEvent("resized", this, newSize);
31798 getBox : function(){
31799 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31802 getMargins : function(){
31803 return this.margins;
31806 updateBox : function(box){
31808 var el = this.activePanel.getEl();
31809 el.dom.style.left = box.x + "px";
31810 el.dom.style.top = box.y + "px";
31811 this.activePanel.setSize(box.width, box.height);
31815 * Returns the container element for this region.
31816 * @return {Roo.Element}
31818 getEl : function(){
31819 return this.activePanel;
31823 * Returns true if this region is currently visible.
31824 * @return {Boolean}
31826 isVisible : function(){
31827 return this.activePanel ? true : false;
31830 setActivePanel : function(panel){
31831 panel = this.getPanel(panel);
31832 if(this.activePanel && this.activePanel != panel){
31833 this.activePanel.setActiveState(false);
31834 this.activePanel.getEl().setLeftTop(-10000,-10000);
31836 this.activePanel = panel;
31837 panel.setActiveState(true);
31839 panel.setSize(this.box.width, this.box.height);
31841 this.fireEvent("panelactivated", this, panel);
31842 this.fireEvent("invalidated");
31846 * Show the specified panel.
31847 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31848 * @return {Roo.ContentPanel} The shown panel or null
31850 showPanel : function(panel){
31851 panel = this.getPanel(panel);
31853 this.setActivePanel(panel);
31859 * Get the active panel for this region.
31860 * @return {Roo.ContentPanel} The active panel or null
31862 getActivePanel : function(){
31863 return this.activePanel;
31867 * Add the passed ContentPanel(s)
31868 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31869 * @return {Roo.ContentPanel} The panel added (if only one was added)
31871 add : function(panel){
31872 if(arguments.length > 1){
31873 for(var i = 0, len = arguments.length; i < len; i++) {
31874 this.add(arguments[i]);
31878 if(this.hasPanel(panel)){
31879 this.showPanel(panel);
31882 var el = panel.getEl();
31883 if(el.dom.parentNode != this.mgr.el.dom){
31884 this.mgr.el.dom.appendChild(el.dom);
31886 if(panel.setRegion){
31887 panel.setRegion(this);
31889 this.panels.add(panel);
31890 el.setStyle("position", "absolute");
31891 if(!panel.background){
31892 this.setActivePanel(panel);
31893 if(this.config.initialSize && this.panels.getCount()==1){
31894 this.resizeTo(this.config.initialSize);
31897 this.fireEvent("paneladded", this, panel);
31902 * Returns true if the panel is in this region.
31903 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31904 * @return {Boolean}
31906 hasPanel : function(panel){
31907 if(typeof panel == "object"){ // must be panel obj
31908 panel = panel.getId();
31910 return this.getPanel(panel) ? true : false;
31914 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31915 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31916 * @param {Boolean} preservePanel Overrides the config preservePanel option
31917 * @return {Roo.ContentPanel} The panel that was removed
31919 remove : function(panel, preservePanel){
31920 panel = this.getPanel(panel);
31925 this.fireEvent("beforeremove", this, panel, e);
31926 if(e.cancel === true){
31929 var panelId = panel.getId();
31930 this.panels.removeKey(panelId);
31935 * Returns the panel specified or null if it's not in this region.
31936 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31937 * @return {Roo.ContentPanel}
31939 getPanel : function(id){
31940 if(typeof id == "object"){ // must be panel obj
31943 return this.panels.get(id);
31947 * Returns this regions position (north/south/east/west/center).
31950 getPosition: function(){
31951 return this.position;
31955 * Ext JS Library 1.1.1
31956 * Copyright(c) 2006-2007, Ext JS, LLC.
31958 * Originally Released Under LGPL - original licence link has changed is not relivant.
31961 * <script type="text/javascript">
31965 * @class Roo.bootstrap.layout.Region
31966 * @extends Roo.bootstrap.layout.Basic
31967 * This class represents a region in a layout manager.
31969 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31970 * @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})
31971 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
31972 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
31973 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
31974 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
31975 * @cfg {String} title The title for the region (overrides panel titles)
31976 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
31977 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31978 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
31979 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31980 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
31981 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31982 * the space available, similar to FireFox 1.5 tabs (defaults to false)
31983 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
31984 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
31985 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
31987 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
31988 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
31989 * @cfg {Boolean} disableTabTips True to disable tab tooltips
31990 * @cfg {Number} width For East/West panels
31991 * @cfg {Number} height For North/South panels
31992 * @cfg {Boolean} split To show the splitter
31993 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
31995 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31996 * @cfg {string} region the region that it inhabits..
31999 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32000 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32002 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32003 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32004 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32006 Roo.bootstrap.layout.Region = function(config)
32009 var mgr = config.mgr;
32010 var pos = config.region;
32011 config.skipConfig = true;
32012 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32013 var dh = Roo.DomHelper;
32014 /** This region's container element
32015 * @type Roo.Element */
32016 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "roo-layout-region roo-layout-panel roo-layout-panel-" + this.position}, true);
32017 /** This region's title element
32018 * @type Roo.Element */
32020 this.titleEl = dh.append(this.el.dom,
32023 unselectable: "on",
32024 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32026 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32027 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32030 this.titleEl.enableDisplayMode();
32031 /** This region's title text element
32032 * @type HTMLElement */
32033 this.titleTextEl = this.titleEl.dom.firstChild;
32034 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32036 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32037 this.closeBtn.enableDisplayMode();
32038 this.closeBtn.on("click", this.closeClicked, this);
32039 this.closeBtn.hide();
32041 this.createBody(config);
32042 this.visible = true;
32043 this.collapsed = false;
32045 if(config.hideWhenEmpty){
32047 this.on("paneladded", this.validateVisibility, this);
32048 this.on("panelremoved", this.validateVisibility, this);
32050 this.applyConfig(config);
32053 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32057 createBody : function(){
32058 /** This region's body element
32059 * @type Roo.Element */
32060 this.bodyEl = this.el.createChild({
32062 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32066 applyConfig : function(c)
32069 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32070 var dh = Roo.DomHelper;
32071 if(c.titlebar !== false){
32072 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32073 this.collapseBtn.on("click", this.collapse, this);
32074 this.collapseBtn.enableDisplayMode();
32076 if(c.showPin === true || this.showPin){
32077 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32078 this.stickBtn.enableDisplayMode();
32079 this.stickBtn.on("click", this.expand, this);
32080 this.stickBtn.hide();
32085 /** This region's collapsed element
32086 * @type Roo.Element */
32089 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32090 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32093 if(c.floatable !== false){
32094 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32095 this.collapsedEl.on("click", this.collapseClick, this);
32098 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32099 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32100 id: "message", unselectable: "on", style:{"float":"left"}});
32101 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32103 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32104 this.expandBtn.on("click", this.expand, this);
32108 if(this.collapseBtn){
32109 this.collapseBtn.setVisible(c.collapsible == true);
32112 this.cmargins = c.cmargins || this.cmargins ||
32113 (this.position == "west" || this.position == "east" ?
32114 {top: 0, left: 2, right:2, bottom: 0} :
32115 {top: 2, left: 0, right:0, bottom: 2});
32117 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32120 this.bottomTabs = c.tabPosition != "top";
32122 this.autoScroll = c.autoScroll || false;
32125 if(this.autoScroll){
32126 this.bodyEl.setStyle("overflow", "auto");
32128 this.bodyEl.setStyle("overflow", c.overflow || 'hidden');
32130 //if(c.titlebar !== false){
32131 if((!c.titlebar && !c.title) || c.titlebar === false){
32132 this.titleEl.hide();
32134 this.titleEl.show();
32136 this.titleTextEl.innerHTML = c.title;
32140 this.duration = c.duration || .30;
32141 this.slideDuration = c.slideDuration || .45;
32144 this.collapse(true);
32151 * Returns true if this region is currently visible.
32152 * @return {Boolean}
32154 isVisible : function(){
32155 return this.visible;
32159 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32160 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32162 //setCollapsedTitle : function(title){
32163 // title = title || " ";
32164 // if(this.collapsedTitleTextEl){
32165 // this.collapsedTitleTextEl.innerHTML = title;
32169 getBox : function(){
32171 // if(!this.collapsed){
32172 b = this.el.getBox(false, true);
32174 // b = this.collapsedEl.getBox(false, true);
32179 getMargins : function(){
32180 return this.margins;
32181 //return this.collapsed ? this.cmargins : this.margins;
32184 highlight : function(){
32185 this.el.addClass("x-layout-panel-dragover");
32188 unhighlight : function(){
32189 this.el.removeClass("x-layout-panel-dragover");
32192 updateBox : function(box)
32195 if(!this.collapsed){
32196 this.el.dom.style.left = box.x + "px";
32197 this.el.dom.style.top = box.y + "px";
32198 this.updateBody(box.width, box.height);
32200 this.collapsedEl.dom.style.left = box.x + "px";
32201 this.collapsedEl.dom.style.top = box.y + "px";
32202 this.collapsedEl.setSize(box.width, box.height);
32205 this.tabs.autoSizeTabs();
32209 updateBody : function(w, h)
32212 this.el.setWidth(w);
32213 w -= this.el.getBorderWidth("rl");
32214 if(this.config.adjustments){
32215 w += this.config.adjustments[0];
32219 this.el.setHeight(h);
32220 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32221 h -= this.el.getBorderWidth("tb");
32222 if(this.config.adjustments){
32223 h += this.config.adjustments[1];
32225 this.bodyEl.setHeight(h);
32227 h = this.tabs.syncHeight(h);
32230 if(this.panelSize){
32231 w = w !== null ? w : this.panelSize.width;
32232 h = h !== null ? h : this.panelSize.height;
32234 if(this.activePanel){
32235 var el = this.activePanel.getEl();
32236 w = w !== null ? w : el.getWidth();
32237 h = h !== null ? h : el.getHeight();
32238 this.panelSize = {width: w, height: h};
32239 this.activePanel.setSize(w, h);
32241 if(Roo.isIE && this.tabs){
32242 this.tabs.el.repaint();
32247 * Returns the container element for this region.
32248 * @return {Roo.Element}
32250 getEl : function(){
32255 * Hides this region.
32258 //if(!this.collapsed){
32259 this.el.dom.style.left = "-2000px";
32262 // this.collapsedEl.dom.style.left = "-2000px";
32263 // this.collapsedEl.hide();
32265 this.visible = false;
32266 this.fireEvent("visibilitychange", this, false);
32270 * Shows this region if it was previously hidden.
32273 //if(!this.collapsed){
32276 // this.collapsedEl.show();
32278 this.visible = true;
32279 this.fireEvent("visibilitychange", this, true);
32282 closeClicked : function(){
32283 if(this.activePanel){
32284 this.remove(this.activePanel);
32288 collapseClick : function(e){
32290 e.stopPropagation();
32293 e.stopPropagation();
32299 * Collapses this region.
32300 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32303 collapse : function(skipAnim, skipCheck = false){
32304 if(this.collapsed) {
32308 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32310 this.collapsed = true;
32312 this.split.el.hide();
32314 if(this.config.animate && skipAnim !== true){
32315 this.fireEvent("invalidated", this);
32316 this.animateCollapse();
32318 this.el.setLocation(-20000,-20000);
32320 this.collapsedEl.show();
32321 this.fireEvent("collapsed", this);
32322 this.fireEvent("invalidated", this);
32328 animateCollapse : function(){
32333 * Expands this region if it was previously collapsed.
32334 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32335 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32338 expand : function(e, skipAnim){
32340 e.stopPropagation();
32342 if(!this.collapsed || this.el.hasActiveFx()) {
32346 this.afterSlideIn();
32349 this.collapsed = false;
32350 if(this.config.animate && skipAnim !== true){
32351 this.animateExpand();
32355 this.split.el.show();
32357 this.collapsedEl.setLocation(-2000,-2000);
32358 this.collapsedEl.hide();
32359 this.fireEvent("invalidated", this);
32360 this.fireEvent("expanded", this);
32364 animateExpand : function(){
32368 initTabs : function()
32370 this.bodyEl.setStyle("overflow", "hidden");
32371 var ts = new Roo.bootstrap.panel.Tabs({
32372 el: this.bodyEl.dom,
32373 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32374 disableTooltips: this.config.disableTabTips,
32375 toolbar : this.config.toolbar
32378 if(this.config.hideTabs){
32379 ts.stripWrap.setDisplayed(false);
32382 ts.resizeTabs = this.config.resizeTabs === true;
32383 ts.minTabWidth = this.config.minTabWidth || 40;
32384 ts.maxTabWidth = this.config.maxTabWidth || 250;
32385 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32386 ts.monitorResize = false;
32387 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32388 ts.bodyEl.addClass('roo-layout-tabs-body');
32389 this.panels.each(this.initPanelAsTab, this);
32392 initPanelAsTab : function(panel){
32393 var ti = this.tabs.addTab(
32395 panel.getTitle(), null,
32396 this.config.closeOnTab && panel.isClosable()
32398 if(panel.tabTip !== undefined){
32399 ti.setTooltip(panel.tabTip);
32401 ti.on("activate", function(){
32402 this.setActivePanel(panel);
32405 if(this.config.closeOnTab){
32406 ti.on("beforeclose", function(t, e){
32408 this.remove(panel);
32414 updatePanelTitle : function(panel, title)
32416 if(this.activePanel == panel){
32417 this.updateTitle(title);
32420 var ti = this.tabs.getTab(panel.getEl().id);
32422 if(panel.tabTip !== undefined){
32423 ti.setTooltip(panel.tabTip);
32428 updateTitle : function(title){
32429 if(this.titleTextEl && !this.config.title){
32430 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32434 setActivePanel : function(panel)
32436 panel = this.getPanel(panel);
32437 if(this.activePanel && this.activePanel != panel){
32438 this.activePanel.setActiveState(false);
32440 this.activePanel = panel;
32441 panel.setActiveState(true);
32442 if(this.panelSize){
32443 panel.setSize(this.panelSize.width, this.panelSize.height);
32446 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32448 this.updateTitle(panel.getTitle());
32450 this.fireEvent("invalidated", this);
32452 this.fireEvent("panelactivated", this, panel);
32456 * Shows the specified panel.
32457 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32458 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32460 showPanel : function(panel)
32462 panel = this.getPanel(panel);
32465 var tab = this.tabs.getTab(panel.getEl().id);
32466 if(tab.isHidden()){
32467 this.tabs.unhideTab(tab.id);
32471 this.setActivePanel(panel);
32478 * Get the active panel for this region.
32479 * @return {Roo.ContentPanel} The active panel or null
32481 getActivePanel : function(){
32482 return this.activePanel;
32485 validateVisibility : function(){
32486 if(this.panels.getCount() < 1){
32487 this.updateTitle(" ");
32488 this.closeBtn.hide();
32491 if(!this.isVisible()){
32498 * Adds the passed ContentPanel(s) to this region.
32499 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32500 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32502 add : function(panel){
32503 if(arguments.length > 1){
32504 for(var i = 0, len = arguments.length; i < len; i++) {
32505 this.add(arguments[i]);
32509 if(this.hasPanel(panel)){
32510 this.showPanel(panel);
32513 panel.setRegion(this);
32514 this.panels.add(panel);
32515 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32516 this.bodyEl.dom.appendChild(panel.getEl().dom);
32517 if(panel.background !== true){
32518 this.setActivePanel(panel);
32520 this.fireEvent("paneladded", this, panel);
32526 this.initPanelAsTab(panel);
32530 if(panel.background !== true){
32531 this.tabs.activate(panel.getEl().id);
32533 this.fireEvent("paneladded", this, panel);
32538 * Hides the tab for the specified panel.
32539 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32541 hidePanel : function(panel){
32542 if(this.tabs && (panel = this.getPanel(panel))){
32543 this.tabs.hideTab(panel.getEl().id);
32548 * Unhides the tab for a previously hidden panel.
32549 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32551 unhidePanel : function(panel){
32552 if(this.tabs && (panel = this.getPanel(panel))){
32553 this.tabs.unhideTab(panel.getEl().id);
32557 clearPanels : function(){
32558 while(this.panels.getCount() > 0){
32559 this.remove(this.panels.first());
32564 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32565 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32566 * @param {Boolean} preservePanel Overrides the config preservePanel option
32567 * @return {Roo.ContentPanel} The panel that was removed
32569 remove : function(panel, preservePanel)
32571 panel = this.getPanel(panel);
32576 this.fireEvent("beforeremove", this, panel, e);
32577 if(e.cancel === true){
32580 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32581 var panelId = panel.getId();
32582 this.panels.removeKey(panelId);
32584 document.body.appendChild(panel.getEl().dom);
32587 this.tabs.removeTab(panel.getEl().id);
32588 }else if (!preservePanel){
32589 this.bodyEl.dom.removeChild(panel.getEl().dom);
32591 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32592 var p = this.panels.first();
32593 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32594 tempEl.appendChild(p.getEl().dom);
32595 this.bodyEl.update("");
32596 this.bodyEl.dom.appendChild(p.getEl().dom);
32598 this.updateTitle(p.getTitle());
32600 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32601 this.setActivePanel(p);
32603 panel.setRegion(null);
32604 if(this.activePanel == panel){
32605 this.activePanel = null;
32607 if(this.config.autoDestroy !== false && preservePanel !== true){
32608 try{panel.destroy();}catch(e){}
32610 this.fireEvent("panelremoved", this, panel);
32615 * Returns the TabPanel component used by this region
32616 * @return {Roo.TabPanel}
32618 getTabs : function(){
32622 createTool : function(parentEl, className){
32623 var btn = Roo.DomHelper.append(parentEl, {
32625 cls: "x-layout-tools-button",
32628 cls: "roo-layout-tools-button-inner " + className,
32632 btn.addClassOnOver("roo-layout-tools-button-over");
32637 * Ext JS Library 1.1.1
32638 * Copyright(c) 2006-2007, Ext JS, LLC.
32640 * Originally Released Under LGPL - original licence link has changed is not relivant.
32643 * <script type="text/javascript">
32649 * @class Roo.SplitLayoutRegion
32650 * @extends Roo.LayoutRegion
32651 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32653 Roo.bootstrap.layout.Split = function(config){
32654 this.cursor = config.cursor;
32655 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32658 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32660 splitTip : "Drag to resize.",
32661 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32662 useSplitTips : false,
32664 applyConfig : function(config){
32665 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32671 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, {
32673 id: this.el.id + "-split",
32674 cls: "roo-layout-split roo-layout-split-"+this.position,
32677 /** The SplitBar for this region
32678 * @type Roo.SplitBar */
32679 // does not exist yet...
32680 Roo.log([this.position, this.orientation]);
32682 this.split = new Roo.bootstrap.SplitBar({
32683 dragElement : splitEl,
32684 resizingElement: this.el,
32685 orientation : this.orientation
32688 this.split.on("moved", this.onSplitMove, this);
32689 this.split.useShim = config.useShim === true;
32690 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32691 if(this.useSplitTips){
32692 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32694 //if(config.collapsible){
32695 // this.split.el.on("dblclick", this.collapse, this);
32698 if(typeof config.minSize != "undefined"){
32699 this.split.minSize = config.minSize;
32701 if(typeof config.maxSize != "undefined"){
32702 this.split.maxSize = config.maxSize;
32704 if(config.hideWhenEmpty || config.hidden || config.collapsed){
32705 this.hideSplitter();
32710 getHMaxSize : function(){
32711 var cmax = this.config.maxSize || 10000;
32712 var center = this.mgr.getRegion("center");
32713 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32716 getVMaxSize : function(){
32717 var cmax = this.config.maxSize || 10000;
32718 var center = this.mgr.getRegion("center");
32719 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32722 onSplitMove : function(split, newSize){
32723 this.fireEvent("resized", this, newSize);
32727 * Returns the {@link Roo.SplitBar} for this region.
32728 * @return {Roo.SplitBar}
32730 getSplitBar : function(){
32735 this.hideSplitter();
32736 Roo.bootstrap.layout.Split.superclass.hide.call(this);
32739 hideSplitter : function(){
32741 this.split.el.setLocation(-2000,-2000);
32742 this.split.el.hide();
32748 this.split.el.show();
32750 Roo.bootstrap.layout.Split.superclass.show.call(this);
32753 beforeSlide: function(){
32754 if(Roo.isGecko){// firefox overflow auto bug workaround
32755 this.bodyEl.clip();
32757 this.tabs.bodyEl.clip();
32759 if(this.activePanel){
32760 this.activePanel.getEl().clip();
32762 if(this.activePanel.beforeSlide){
32763 this.activePanel.beforeSlide();
32769 afterSlide : function(){
32770 if(Roo.isGecko){// firefox overflow auto bug workaround
32771 this.bodyEl.unclip();
32773 this.tabs.bodyEl.unclip();
32775 if(this.activePanel){
32776 this.activePanel.getEl().unclip();
32777 if(this.activePanel.afterSlide){
32778 this.activePanel.afterSlide();
32784 initAutoHide : function(){
32785 if(this.autoHide !== false){
32786 if(!this.autoHideHd){
32787 var st = new Roo.util.DelayedTask(this.slideIn, this);
32788 this.autoHideHd = {
32789 "mouseout": function(e){
32790 if(!e.within(this.el, true)){
32794 "mouseover" : function(e){
32800 this.el.on(this.autoHideHd);
32804 clearAutoHide : function(){
32805 if(this.autoHide !== false){
32806 this.el.un("mouseout", this.autoHideHd.mouseout);
32807 this.el.un("mouseover", this.autoHideHd.mouseover);
32811 clearMonitor : function(){
32812 Roo.get(document).un("click", this.slideInIf, this);
32815 // these names are backwards but not changed for compat
32816 slideOut : function(){
32817 if(this.isSlid || this.el.hasActiveFx()){
32820 this.isSlid = true;
32821 if(this.collapseBtn){
32822 this.collapseBtn.hide();
32824 this.closeBtnState = this.closeBtn.getStyle('display');
32825 this.closeBtn.hide();
32827 this.stickBtn.show();
32830 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32831 this.beforeSlide();
32832 this.el.setStyle("z-index", 10001);
32833 this.el.slideIn(this.getSlideAnchor(), {
32834 callback: function(){
32836 this.initAutoHide();
32837 Roo.get(document).on("click", this.slideInIf, this);
32838 this.fireEvent("slideshow", this);
32845 afterSlideIn : function(){
32846 this.clearAutoHide();
32847 this.isSlid = false;
32848 this.clearMonitor();
32849 this.el.setStyle("z-index", "");
32850 if(this.collapseBtn){
32851 this.collapseBtn.show();
32853 this.closeBtn.setStyle('display', this.closeBtnState);
32855 this.stickBtn.hide();
32857 this.fireEvent("slidehide", this);
32860 slideIn : function(cb){
32861 if(!this.isSlid || this.el.hasActiveFx()){
32865 this.isSlid = false;
32866 this.beforeSlide();
32867 this.el.slideOut(this.getSlideAnchor(), {
32868 callback: function(){
32869 this.el.setLeftTop(-10000, -10000);
32871 this.afterSlideIn();
32879 slideInIf : function(e){
32880 if(!e.within(this.el)){
32885 animateCollapse : function(){
32886 this.beforeSlide();
32887 this.el.setStyle("z-index", 20000);
32888 var anchor = this.getSlideAnchor();
32889 this.el.slideOut(anchor, {
32890 callback : function(){
32891 this.el.setStyle("z-index", "");
32892 this.collapsedEl.slideIn(anchor, {duration:.3});
32894 this.el.setLocation(-10000,-10000);
32896 this.fireEvent("collapsed", this);
32903 animateExpand : function(){
32904 this.beforeSlide();
32905 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32906 this.el.setStyle("z-index", 20000);
32907 this.collapsedEl.hide({
32910 this.el.slideIn(this.getSlideAnchor(), {
32911 callback : function(){
32912 this.el.setStyle("z-index", "");
32915 this.split.el.show();
32917 this.fireEvent("invalidated", this);
32918 this.fireEvent("expanded", this);
32946 getAnchor : function(){
32947 return this.anchors[this.position];
32950 getCollapseAnchor : function(){
32951 return this.canchors[this.position];
32954 getSlideAnchor : function(){
32955 return this.sanchors[this.position];
32958 getAlignAdj : function(){
32959 var cm = this.cmargins;
32960 switch(this.position){
32976 getExpandAdj : function(){
32977 var c = this.collapsedEl, cm = this.cmargins;
32978 switch(this.position){
32980 return [-(cm.right+c.getWidth()+cm.left), 0];
32983 return [cm.right+c.getWidth()+cm.left, 0];
32986 return [0, -(cm.top+cm.bottom+c.getHeight())];
32989 return [0, cm.top+cm.bottom+c.getHeight()];
32995 * Ext JS Library 1.1.1
32996 * Copyright(c) 2006-2007, Ext JS, LLC.
32998 * Originally Released Under LGPL - original licence link has changed is not relivant.
33001 * <script type="text/javascript">
33004 * These classes are private internal classes
33006 Roo.bootstrap.layout.Center = function(config){
33007 config.region = "center";
33008 Roo.bootstrap.layout.Region.call(this, config);
33009 this.visible = true;
33010 this.minWidth = config.minWidth || 20;
33011 this.minHeight = config.minHeight || 20;
33014 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33016 // center panel can't be hidden
33020 // center panel can't be hidden
33023 getMinWidth: function(){
33024 return this.minWidth;
33027 getMinHeight: function(){
33028 return this.minHeight;
33041 Roo.bootstrap.layout.North = function(config)
33043 config.region = 'north';
33044 config.cursor = 'n-resize';
33046 Roo.bootstrap.layout.Split.call(this, config);
33048 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33049 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33050 this.split.el.addClass("roo-layout-split-v");
33052 var size = config.initialSize || config.height;
33053 if(typeof size != "undefined"){
33054 this.el.setHeight(size);
33057 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33059 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33060 getBox : function(){
33061 if(this.collapsed){
33062 return this.collapsedEl.getBox();
33064 var box = this.el.getBox();
33066 box.height += this.split.el.getHeight();
33071 updateBox : function(box){
33072 if(this.split && !this.collapsed){
33073 box.height -= this.split.el.getHeight();
33074 this.split.el.setLeft(box.x);
33075 this.split.el.setTop(box.y+box.height);
33076 this.split.el.setWidth(box.width);
33078 if(this.collapsed){
33079 this.updateBody(box.width, null);
33081 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33089 Roo.bootstrap.layout.South = function(config){
33090 config.region = 'south';
33091 config.cursor = 's-resize';
33092 Roo.bootstrap.layout.Split.call(this, config);
33094 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33095 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33096 this.split.el.addClass("roo-layout-split-v");
33098 var size = config.initialSize || config.height;
33099 if(typeof size != "undefined"){
33100 this.el.setHeight(size);
33104 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33105 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33106 getBox : function(){
33107 if(this.collapsed){
33108 return this.collapsedEl.getBox();
33110 var box = this.el.getBox();
33112 var sh = this.split.el.getHeight();
33119 updateBox : function(box){
33120 if(this.split && !this.collapsed){
33121 var sh = this.split.el.getHeight();
33124 this.split.el.setLeft(box.x);
33125 this.split.el.setTop(box.y-sh);
33126 this.split.el.setWidth(box.width);
33128 if(this.collapsed){
33129 this.updateBody(box.width, null);
33131 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33135 Roo.bootstrap.layout.East = function(config){
33136 config.region = "east";
33137 config.cursor = "e-resize";
33138 Roo.bootstrap.layout.Split.call(this, config);
33140 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33141 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33142 this.split.el.addClass("roo-layout-split-h");
33144 var size = config.initialSize || config.width;
33145 if(typeof size != "undefined"){
33146 this.el.setWidth(size);
33149 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33150 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33151 getBox : function(){
33152 if(this.collapsed){
33153 return this.collapsedEl.getBox();
33155 var box = this.el.getBox();
33157 var sw = this.split.el.getWidth();
33164 updateBox : function(box){
33165 if(this.split && !this.collapsed){
33166 var sw = this.split.el.getWidth();
33168 this.split.el.setLeft(box.x);
33169 this.split.el.setTop(box.y);
33170 this.split.el.setHeight(box.height);
33173 if(this.collapsed){
33174 this.updateBody(null, box.height);
33176 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33180 Roo.bootstrap.layout.West = function(config){
33181 config.region = "west";
33182 config.cursor = "w-resize";
33184 Roo.bootstrap.layout.Split.call(this, config);
33186 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33187 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33188 this.split.el.addClass("roo-layout-split-h");
33190 var size = config.initialSize || config.width;
33191 if(typeof size != "undefined"){
33192 this.el.setWidth(size);
33195 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33196 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33197 getBox : function(){
33198 if(this.collapsed){
33199 return this.collapsedEl.getBox();
33201 var box = this.el.getBox();
33203 box.width += this.split.el.getWidth();
33208 updateBox : function(box){
33209 if(this.split && !this.collapsed){
33210 var sw = this.split.el.getWidth();
33212 this.split.el.setLeft(box.x+box.width);
33213 this.split.el.setTop(box.y);
33214 this.split.el.setHeight(box.height);
33216 if(this.collapsed){
33217 this.updateBody(null, box.height);
33219 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33222 Roo.namespace("Roo.bootstrap.panel");/*
33224 * Ext JS Library 1.1.1
33225 * Copyright(c) 2006-2007, Ext JS, LLC.
33227 * Originally Released Under LGPL - original licence link has changed is not relivant.
33230 * <script type="text/javascript">
33233 * @class Roo.ContentPanel
33234 * @extends Roo.util.Observable
33235 * A basic ContentPanel element.
33236 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33237 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33238 * @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
33239 * @cfg {Boolean} closable True if the panel can be closed/removed
33240 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33241 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33242 * @cfg {Toolbar} toolbar A toolbar for this panel
33243 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33244 * @cfg {String} title The title for this panel
33245 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33246 * @cfg {String} url Calls {@link #setUrl} with this value
33247 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33248 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33249 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33250 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33253 * Create a new ContentPanel.
33254 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33255 * @param {String/Object} config A string to set only the title or a config object
33256 * @param {String} content (optional) Set the HTML content for this panel
33257 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33259 Roo.bootstrap.panel.Content = function( config){
33261 var el = config.el;
33262 var content = config.content;
33264 if(config.autoCreate){ // xtype is available if this is called from factory
33267 this.el = Roo.get(el);
33268 if(!this.el && config && config.autoCreate){
33269 if(typeof config.autoCreate == "object"){
33270 if(!config.autoCreate.id){
33271 config.autoCreate.id = config.id||el;
33273 this.el = Roo.DomHelper.append(document.body,
33274 config.autoCreate, true);
33276 var elcfg = { tag: "div",
33277 cls: "roo-layout-inactive-content",
33281 elcfg.html = config.html;
33285 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33288 this.closable = false;
33289 this.loaded = false;
33290 this.active = false;
33291 if(typeof config == "string"){
33292 this.title = config;
33294 Roo.apply(this, config);
33297 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33298 this.wrapEl = this.el.wrap();
33299 this.toolbar.container = this.el.insertSibling(false, 'before');
33300 this.toolbar = new Roo.Toolbar(this.toolbar);
33303 // xtype created footer. - not sure if will work as we normally have to render first..
33304 if (this.footer && !this.footer.el && this.footer.xtype) {
33305 if (!this.wrapEl) {
33306 this.wrapEl = this.el.wrap();
33309 this.footer.container = this.wrapEl.createChild();
33311 this.footer = Roo.factory(this.footer, Roo);
33316 this.resizeEl = Roo.get(this.resizeEl, true);
33318 this.resizeEl = this.el;
33320 // handle view.xtype
33328 * Fires when this panel is activated.
33329 * @param {Roo.ContentPanel} this
33333 * @event deactivate
33334 * Fires when this panel is activated.
33335 * @param {Roo.ContentPanel} this
33337 "deactivate" : true,
33341 * Fires when this panel is resized if fitToFrame is true.
33342 * @param {Roo.ContentPanel} this
33343 * @param {Number} width The width after any component adjustments
33344 * @param {Number} height The height after any component adjustments
33350 * Fires when this tab is created
33351 * @param {Roo.ContentPanel} this
33362 if(this.autoScroll){
33363 this.resizeEl.setStyle("overflow", "auto");
33365 // fix randome scrolling
33366 this.el.on('scroll', function() {
33367 Roo.log('fix random scolling');
33368 this.scrollTo('top',0);
33371 content = content || this.content;
33373 this.setContent(content);
33375 if(config && config.url){
33376 this.setUrl(this.url, this.params, this.loadOnce);
33381 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33383 if (this.view && typeof(this.view.xtype) != 'undefined') {
33384 this.view.el = this.el.appendChild(document.createElement("div"));
33385 this.view = Roo.factory(this.view);
33386 this.view.render && this.view.render(false, '');
33390 this.fireEvent('render', this);
33393 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33395 setRegion : function(region){
33396 this.region = region;
33398 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33400 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33405 * Returns the toolbar for this Panel if one was configured.
33406 * @return {Roo.Toolbar}
33408 getToolbar : function(){
33409 return this.toolbar;
33412 setActiveState : function(active){
33413 this.active = active;
33415 this.fireEvent("deactivate", this);
33417 this.fireEvent("activate", this);
33421 * Updates this panel's element
33422 * @param {String} content The new content
33423 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33425 setContent : function(content, loadScripts){
33426 this.el.update(content, loadScripts);
33429 ignoreResize : function(w, h){
33430 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33433 this.lastSize = {width: w, height: h};
33438 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33439 * @return {Roo.UpdateManager} The UpdateManager
33441 getUpdateManager : function(){
33442 return this.el.getUpdateManager();
33445 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33446 * @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:
33449 url: "your-url.php",
33450 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33451 callback: yourFunction,
33452 scope: yourObject, //(optional scope)
33455 text: "Loading...",
33460 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33461 * 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.
33462 * @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}
33463 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33464 * @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.
33465 * @return {Roo.ContentPanel} this
33468 var um = this.el.getUpdateManager();
33469 um.update.apply(um, arguments);
33475 * 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.
33476 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33477 * @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)
33478 * @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)
33479 * @return {Roo.UpdateManager} The UpdateManager
33481 setUrl : function(url, params, loadOnce){
33482 if(this.refreshDelegate){
33483 this.removeListener("activate", this.refreshDelegate);
33485 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33486 this.on("activate", this.refreshDelegate);
33487 return this.el.getUpdateManager();
33490 _handleRefresh : function(url, params, loadOnce){
33491 if(!loadOnce || !this.loaded){
33492 var updater = this.el.getUpdateManager();
33493 updater.update(url, params, this._setLoaded.createDelegate(this));
33497 _setLoaded : function(){
33498 this.loaded = true;
33502 * Returns this panel's id
33505 getId : function(){
33510 * Returns this panel's element - used by regiosn to add.
33511 * @return {Roo.Element}
33513 getEl : function(){
33514 return this.wrapEl || this.el;
33519 adjustForComponents : function(width, height)
33521 //Roo.log('adjustForComponents ');
33522 if(this.resizeEl != this.el){
33523 width -= this.el.getFrameWidth('lr');
33524 height -= this.el.getFrameWidth('tb');
33527 var te = this.toolbar.getEl();
33528 height -= te.getHeight();
33529 te.setWidth(width);
33532 var te = this.footer.getEl();
33533 Roo.log("footer:" + te.getHeight());
33535 height -= te.getHeight();
33536 te.setWidth(width);
33540 if(this.adjustments){
33541 width += this.adjustments[0];
33542 height += this.adjustments[1];
33544 return {"width": width, "height": height};
33547 setSize : function(width, height){
33548 if(this.fitToFrame && !this.ignoreResize(width, height)){
33549 if(this.fitContainer && this.resizeEl != this.el){
33550 this.el.setSize(width, height);
33552 var size = this.adjustForComponents(width, height);
33553 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33554 this.fireEvent('resize', this, size.width, size.height);
33559 * Returns this panel's title
33562 getTitle : function(){
33567 * Set this panel's title
33568 * @param {String} title
33570 setTitle : function(title){
33571 this.title = title;
33573 this.region.updatePanelTitle(this, title);
33578 * Returns true is this panel was configured to be closable
33579 * @return {Boolean}
33581 isClosable : function(){
33582 return this.closable;
33585 beforeSlide : function(){
33587 this.resizeEl.clip();
33590 afterSlide : function(){
33592 this.resizeEl.unclip();
33596 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33597 * Will fail silently if the {@link #setUrl} method has not been called.
33598 * This does not activate the panel, just updates its content.
33600 refresh : function(){
33601 if(this.refreshDelegate){
33602 this.loaded = false;
33603 this.refreshDelegate();
33608 * Destroys this panel
33610 destroy : function(){
33611 this.el.removeAllListeners();
33612 var tempEl = document.createElement("span");
33613 tempEl.appendChild(this.el.dom);
33614 tempEl.innerHTML = "";
33620 * form - if the content panel contains a form - this is a reference to it.
33621 * @type {Roo.form.Form}
33625 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33626 * This contains a reference to it.
33632 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33642 * @param {Object} cfg Xtype definition of item to add.
33646 getChildContainer: function () {
33647 return this.getEl();
33652 var ret = new Roo.factory(cfg);
33657 if (cfg.xtype.match(/^Form$/)) {
33660 //if (this.footer) {
33661 // el = this.footer.container.insertSibling(false, 'before');
33663 el = this.el.createChild();
33666 this.form = new Roo.form.Form(cfg);
33669 if ( this.form.allItems.length) {
33670 this.form.render(el.dom);
33674 // should only have one of theses..
33675 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33676 // views.. should not be just added - used named prop 'view''
33678 cfg.el = this.el.appendChild(document.createElement("div"));
33681 var ret = new Roo.factory(cfg);
33683 ret.render && ret.render(false, ''); // render blank..
33693 * @class Roo.bootstrap.panel.Grid
33694 * @extends Roo.bootstrap.panel.Content
33696 * Create a new GridPanel.
33697 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33698 * @param {String/Object} config A string to set only the panel's title, or a config object
33700 new Roo.bootstrap.panel.Grid({
33709 Roo.bootstrap.panel.Grid = function(config){
33712 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33713 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33715 this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
33716 config.el = this.wrapper;
33718 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33721 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33723 // xtype created footer. - not sure if will work as we normally have to render first..
33724 if (this.footer && !this.footer.el && this.footer.xtype) {
33726 this.footer.container = this.grid.getView().getFooterPanel(true);
33727 this.footer.dataSource = this.grid.dataSource;
33728 this.footer = Roo.factory(this.footer, Roo);
33733 config.grid.monitorWindowResize = false; // turn off autosizing
33734 config.grid.autoHeight = false;
33735 config.grid.autoWidth = false;
33736 this.grid = config.grid;
33737 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33742 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
33743 getId : function(){
33744 return this.grid.id;
33748 * Returns the grid for this panel
33749 * @return {Roo.bootstrap.Table}
33751 getGrid : function(){
33755 setSize : function(width, height){
33756 if(!this.ignoreResize(width, height)){
33757 var grid = this.grid;
33758 var size = this.adjustForComponents(width, height);
33759 grid.getGridEl().setSize(size.width, size.height);
33761 var thd = grid.getGridEl().select('thead',true).first();
33762 var tbd = grid.getGridEl().select('tbody', true).first();
33764 tbd.setSize(width, height - thd.getHeight());
33773 beforeSlide : function(){
33774 this.grid.getView().scroller.clip();
33777 afterSlide : function(){
33778 this.grid.getView().scroller.unclip();
33781 destroy : function(){
33782 this.grid.destroy();
33784 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
33789 * @class Roo.bootstrap.panel.Nest
33790 * @extends Roo.bootstrap.panel.Content
33792 * Create a new Panel, that can contain a layout.Border.
33795 * @param {Roo.BorderLayout} layout The layout for this panel
33796 * @param {String/Object} config A string to set only the title or a config object
33798 Roo.bootstrap.panel.Nest = function(config)
33800 // construct with only one argument..
33801 /* FIXME - implement nicer consturctors
33802 if (layout.layout) {
33804 layout = config.layout;
33805 delete config.layout;
33807 if (layout.xtype && !layout.getEl) {
33808 // then layout needs constructing..
33809 layout = Roo.factory(layout, Roo);
33813 config.el = config.layout.getEl();
33815 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
33817 config.layout.monitorWindowResize = false; // turn off autosizing
33818 this.layout = config.layout;
33819 this.layout.getEl().addClass("roo-layout-nested-layout");
33826 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
33828 setSize : function(width, height){
33829 if(!this.ignoreResize(width, height)){
33830 var size = this.adjustForComponents(width, height);
33831 var el = this.layout.getEl();
33832 el.setSize(size.width, size.height);
33833 var touch = el.dom.offsetWidth;
33834 this.layout.layout();
33835 // ie requires a double layout on the first pass
33836 if(Roo.isIE && !this.initialized){
33837 this.initialized = true;
33838 this.layout.layout();
33843 // activate all subpanels if not currently active..
33845 setActiveState : function(active){
33846 this.active = active;
33848 this.fireEvent("deactivate", this);
33852 this.fireEvent("activate", this);
33853 // not sure if this should happen before or after..
33854 if (!this.layout) {
33855 return; // should not happen..
33858 for (var r in this.layout.regions) {
33859 reg = this.layout.getRegion(r);
33860 if (reg.getActivePanel()) {
33861 //reg.showPanel(reg.getActivePanel()); // force it to activate..
33862 reg.setActivePanel(reg.getActivePanel());
33865 if (!reg.panels.length) {
33868 reg.showPanel(reg.getPanel(0));
33877 * Returns the nested BorderLayout for this panel
33878 * @return {Roo.BorderLayout}
33880 getLayout : function(){
33881 return this.layout;
33885 * Adds a xtype elements to the layout of the nested panel
33889 xtype : 'ContentPanel',
33896 xtype : 'NestedLayoutPanel',
33902 items : [ ... list of content panels or nested layout panels.. ]
33906 * @param {Object} cfg Xtype definition of item to add.
33908 addxtype : function(cfg) {
33909 return this.layout.addxtype(cfg);
33914 * Ext JS Library 1.1.1
33915 * Copyright(c) 2006-2007, Ext JS, LLC.
33917 * Originally Released Under LGPL - original licence link has changed is not relivant.
33920 * <script type="text/javascript">
33923 * @class Roo.TabPanel
33924 * @extends Roo.util.Observable
33925 * A lightweight tab container.
33929 // basic tabs 1, built from existing content
33930 var tabs = new Roo.TabPanel("tabs1");
33931 tabs.addTab("script", "View Script");
33932 tabs.addTab("markup", "View Markup");
33933 tabs.activate("script");
33935 // more advanced tabs, built from javascript
33936 var jtabs = new Roo.TabPanel("jtabs");
33937 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
33939 // set up the UpdateManager
33940 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
33941 var updater = tab2.getUpdateManager();
33942 updater.setDefaultUrl("ajax1.htm");
33943 tab2.on('activate', updater.refresh, updater, true);
33945 // Use setUrl for Ajax loading
33946 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
33947 tab3.setUrl("ajax2.htm", null, true);
33950 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
33953 jtabs.activate("jtabs-1");
33956 * Create a new TabPanel.
33957 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
33958 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
33960 Roo.bootstrap.panel.Tabs = function(config){
33962 * The container element for this TabPanel.
33963 * @type Roo.Element
33965 this.el = Roo.get(config.el);
33968 if(typeof config == "boolean"){
33969 this.tabPosition = config ? "bottom" : "top";
33971 Roo.apply(this, config);
33975 if(this.tabPosition == "bottom"){
33976 this.bodyEl = Roo.get(this.createBody(this.el.dom));
33977 this.el.addClass("roo-tabs-bottom");
33979 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
33980 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
33981 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
33983 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
33985 if(this.tabPosition != "bottom"){
33986 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
33987 * @type Roo.Element
33989 this.bodyEl = Roo.get(this.createBody(this.el.dom));
33990 this.el.addClass("roo-tabs-top");
33994 this.bodyEl.setStyle("position", "relative");
33996 this.active = null;
33997 this.activateDelegate = this.activate.createDelegate(this);
34002 * Fires when the active tab changes
34003 * @param {Roo.TabPanel} this
34004 * @param {Roo.TabPanelItem} activePanel The new active tab
34008 * @event beforetabchange
34009 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34010 * @param {Roo.TabPanel} this
34011 * @param {Object} e Set cancel to true on this object to cancel the tab change
34012 * @param {Roo.TabPanelItem} tab The tab being changed to
34014 "beforetabchange" : true
34017 Roo.EventManager.onWindowResize(this.onResize, this);
34018 this.cpad = this.el.getPadding("lr");
34019 this.hiddenCount = 0;
34022 // toolbar on the tabbar support...
34023 if (this.toolbar) {
34024 alert("no toolbar support yet");
34025 this.toolbar = false;
34027 var tcfg = this.toolbar;
34028 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34029 this.toolbar = new Roo.Toolbar(tcfg);
34030 if (Roo.isSafari) {
34031 var tbl = tcfg.container.child('table', true);
34032 tbl.setAttribute('width', '100%');
34040 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34043 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34045 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34047 tabPosition : "top",
34049 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34051 currentTabWidth : 0,
34053 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34057 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34061 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34063 preferredTabWidth : 175,
34065 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34067 resizeTabs : false,
34069 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34071 monitorResize : true,
34073 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34078 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34079 * @param {String} id The id of the div to use <b>or create</b>
34080 * @param {String} text The text for the tab
34081 * @param {String} content (optional) Content to put in the TabPanelItem body
34082 * @param {Boolean} closable (optional) True to create a close icon on the tab
34083 * @return {Roo.TabPanelItem} The created TabPanelItem
34085 addTab : function(id, text, content, closable)
34087 var item = new Roo.bootstrap.panel.TabItem({
34091 closable : closable
34093 this.addTabItem(item);
34095 item.setContent(content);
34101 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34102 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34103 * @return {Roo.TabPanelItem}
34105 getTab : function(id){
34106 return this.items[id];
34110 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34111 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34113 hideTab : function(id){
34114 var t = this.items[id];
34117 this.hiddenCount++;
34118 this.autoSizeTabs();
34123 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34124 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34126 unhideTab : function(id){
34127 var t = this.items[id];
34129 t.setHidden(false);
34130 this.hiddenCount--;
34131 this.autoSizeTabs();
34136 * Adds an existing {@link Roo.TabPanelItem}.
34137 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34139 addTabItem : function(item){
34140 this.items[item.id] = item;
34141 this.items.push(item);
34142 // if(this.resizeTabs){
34143 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34144 // this.autoSizeTabs();
34146 // item.autoSize();
34151 * Removes a {@link Roo.TabPanelItem}.
34152 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34154 removeTab : function(id){
34155 var items = this.items;
34156 var tab = items[id];
34157 if(!tab) { return; }
34158 var index = items.indexOf(tab);
34159 if(this.active == tab && items.length > 1){
34160 var newTab = this.getNextAvailable(index);
34165 this.stripEl.dom.removeChild(tab.pnode.dom);
34166 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34167 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34169 items.splice(index, 1);
34170 delete this.items[tab.id];
34171 tab.fireEvent("close", tab);
34172 tab.purgeListeners();
34173 this.autoSizeTabs();
34176 getNextAvailable : function(start){
34177 var items = this.items;
34179 // look for a next tab that will slide over to
34180 // replace the one being removed
34181 while(index < items.length){
34182 var item = items[++index];
34183 if(item && !item.isHidden()){
34187 // if one isn't found select the previous tab (on the left)
34190 var item = items[--index];
34191 if(item && !item.isHidden()){
34199 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34200 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34202 disableTab : function(id){
34203 var tab = this.items[id];
34204 if(tab && this.active != tab){
34210 * Enables a {@link Roo.TabPanelItem} that is disabled.
34211 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34213 enableTab : function(id){
34214 var tab = this.items[id];
34219 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34220 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34221 * @return {Roo.TabPanelItem} The TabPanelItem.
34223 activate : function(id){
34224 var tab = this.items[id];
34228 if(tab == this.active || tab.disabled){
34232 this.fireEvent("beforetabchange", this, e, tab);
34233 if(e.cancel !== true && !tab.disabled){
34235 this.active.hide();
34237 this.active = this.items[id];
34238 this.active.show();
34239 this.fireEvent("tabchange", this, this.active);
34245 * Gets the active {@link Roo.TabPanelItem}.
34246 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34248 getActiveTab : function(){
34249 return this.active;
34253 * Updates the tab body element to fit the height of the container element
34254 * for overflow scrolling
34255 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34257 syncHeight : function(targetHeight){
34258 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34259 var bm = this.bodyEl.getMargins();
34260 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34261 this.bodyEl.setHeight(newHeight);
34265 onResize : function(){
34266 if(this.monitorResize){
34267 this.autoSizeTabs();
34272 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34274 beginUpdate : function(){
34275 this.updating = true;
34279 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34281 endUpdate : function(){
34282 this.updating = false;
34283 this.autoSizeTabs();
34287 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34289 autoSizeTabs : function(){
34290 var count = this.items.length;
34291 var vcount = count - this.hiddenCount;
34292 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34295 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34296 var availWidth = Math.floor(w / vcount);
34297 var b = this.stripBody;
34298 if(b.getWidth() > w){
34299 var tabs = this.items;
34300 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34301 if(availWidth < this.minTabWidth){
34302 /*if(!this.sleft){ // incomplete scrolling code
34303 this.createScrollButtons();
34306 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34309 if(this.currentTabWidth < this.preferredTabWidth){
34310 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34316 * Returns the number of tabs in this TabPanel.
34319 getCount : function(){
34320 return this.items.length;
34324 * Resizes all the tabs to the passed width
34325 * @param {Number} The new width
34327 setTabWidth : function(width){
34328 this.currentTabWidth = width;
34329 for(var i = 0, len = this.items.length; i < len; i++) {
34330 if(!this.items[i].isHidden()) {
34331 this.items[i].setWidth(width);
34337 * Destroys this TabPanel
34338 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34340 destroy : function(removeEl){
34341 Roo.EventManager.removeResizeListener(this.onResize, this);
34342 for(var i = 0, len = this.items.length; i < len; i++){
34343 this.items[i].purgeListeners();
34345 if(removeEl === true){
34346 this.el.update("");
34351 createStrip : function(container)
34353 var strip = document.createElement("nav");
34354 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34355 container.appendChild(strip);
34359 createStripList : function(strip)
34361 // div wrapper for retard IE
34362 // returns the "tr" element.
34363 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34364 //'<div class="x-tabs-strip-wrap">'+
34365 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34366 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34367 return strip.firstChild; //.firstChild.firstChild.firstChild;
34369 createBody : function(container)
34371 var body = document.createElement("div");
34372 Roo.id(body, "tab-body");
34373 //Roo.fly(body).addClass("x-tabs-body");
34374 Roo.fly(body).addClass("tab-content");
34375 container.appendChild(body);
34378 createItemBody :function(bodyEl, id){
34379 var body = Roo.getDom(id);
34381 body = document.createElement("div");
34384 //Roo.fly(body).addClass("x-tabs-item-body");
34385 Roo.fly(body).addClass("tab-pane");
34386 bodyEl.insertBefore(body, bodyEl.firstChild);
34390 createStripElements : function(stripEl, text, closable)
34392 var td = document.createElement("li"); // was td..
34393 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34394 //stripEl.appendChild(td);
34396 td.className = "x-tabs-closable";
34397 if(!this.closeTpl){
34398 this.closeTpl = new Roo.Template(
34399 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34400 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34401 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34404 var el = this.closeTpl.overwrite(td, {"text": text});
34405 var close = el.getElementsByTagName("div")[0];
34406 var inner = el.getElementsByTagName("em")[0];
34407 return {"el": el, "close": close, "inner": inner};
34410 // not sure what this is..
34412 //this.tabTpl = new Roo.Template(
34413 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34414 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34416 this.tabTpl = new Roo.Template(
34418 '<span unselectable="on"' +
34419 (this.disableTooltips ? '' : ' title="{text}"') +
34420 ' >{text}</span></span></a>'
34424 var el = this.tabTpl.overwrite(td, {"text": text});
34425 var inner = el.getElementsByTagName("span")[0];
34426 return {"el": el, "inner": inner};
34434 * @class Roo.TabPanelItem
34435 * @extends Roo.util.Observable
34436 * Represents an individual item (tab plus body) in a TabPanel.
34437 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34438 * @param {String} id The id of this TabPanelItem
34439 * @param {String} text The text for the tab of this TabPanelItem
34440 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34442 Roo.bootstrap.panel.TabItem = function(config){
34444 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34445 * @type Roo.TabPanel
34447 this.tabPanel = config.panel;
34449 * The id for this TabPanelItem
34452 this.id = config.id;
34454 this.disabled = false;
34456 this.text = config.text;
34458 this.loaded = false;
34459 this.closable = config.closable;
34462 * The body element for this TabPanelItem.
34463 * @type Roo.Element
34465 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34466 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34467 this.bodyEl.setStyle("display", "block");
34468 this.bodyEl.setStyle("zoom", "1");
34469 //this.hideAction();
34471 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34473 this.el = Roo.get(els.el);
34474 this.inner = Roo.get(els.inner, true);
34475 this.textEl = Roo.get(this.el.dom.firstChild, true);
34476 this.pnode = Roo.get(els.el.parentNode, true);
34477 this.el.on("mousedown", this.onTabMouseDown, this);
34478 this.el.on("click", this.onTabClick, this);
34480 if(config.closable){
34481 var c = Roo.get(els.close, true);
34482 c.dom.title = this.closeText;
34483 c.addClassOnOver("close-over");
34484 c.on("click", this.closeClick, this);
34490 * Fires when this tab becomes the active tab.
34491 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34492 * @param {Roo.TabPanelItem} this
34496 * @event beforeclose
34497 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34498 * @param {Roo.TabPanelItem} this
34499 * @param {Object} e Set cancel to true on this object to cancel the close.
34501 "beforeclose": true,
34504 * Fires when this tab is closed.
34505 * @param {Roo.TabPanelItem} this
34509 * @event deactivate
34510 * Fires when this tab is no longer the active tab.
34511 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34512 * @param {Roo.TabPanelItem} this
34514 "deactivate" : true
34516 this.hidden = false;
34518 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34521 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34523 purgeListeners : function(){
34524 Roo.util.Observable.prototype.purgeListeners.call(this);
34525 this.el.removeAllListeners();
34528 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34531 this.pnode.addClass("active");
34534 this.tabPanel.stripWrap.repaint();
34536 this.fireEvent("activate", this.tabPanel, this);
34540 * Returns true if this tab is the active tab.
34541 * @return {Boolean}
34543 isActive : function(){
34544 return this.tabPanel.getActiveTab() == this;
34548 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34551 this.pnode.removeClass("active");
34553 this.fireEvent("deactivate", this.tabPanel, this);
34556 hideAction : function(){
34557 this.bodyEl.hide();
34558 this.bodyEl.setStyle("position", "absolute");
34559 this.bodyEl.setLeft("-20000px");
34560 this.bodyEl.setTop("-20000px");
34563 showAction : function(){
34564 this.bodyEl.setStyle("position", "relative");
34565 this.bodyEl.setTop("");
34566 this.bodyEl.setLeft("");
34567 this.bodyEl.show();
34571 * Set the tooltip for the tab.
34572 * @param {String} tooltip The tab's tooltip
34574 setTooltip : function(text){
34575 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34576 this.textEl.dom.qtip = text;
34577 this.textEl.dom.removeAttribute('title');
34579 this.textEl.dom.title = text;
34583 onTabClick : function(e){
34584 e.preventDefault();
34585 this.tabPanel.activate(this.id);
34588 onTabMouseDown : function(e){
34589 e.preventDefault();
34590 this.tabPanel.activate(this.id);
34593 getWidth : function(){
34594 return this.inner.getWidth();
34597 setWidth : function(width){
34598 var iwidth = width - this.pnode.getPadding("lr");
34599 this.inner.setWidth(iwidth);
34600 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34601 this.pnode.setWidth(width);
34605 * Show or hide the tab
34606 * @param {Boolean} hidden True to hide or false to show.
34608 setHidden : function(hidden){
34609 this.hidden = hidden;
34610 this.pnode.setStyle("display", hidden ? "none" : "");
34614 * Returns true if this tab is "hidden"
34615 * @return {Boolean}
34617 isHidden : function(){
34618 return this.hidden;
34622 * Returns the text for this tab
34625 getText : function(){
34629 autoSize : function(){
34630 //this.el.beginMeasure();
34631 this.textEl.setWidth(1);
34633 * #2804 [new] Tabs in Roojs
34634 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34636 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34637 //this.el.endMeasure();
34641 * Sets the text for the tab (Note: this also sets the tooltip text)
34642 * @param {String} text The tab's text and tooltip
34644 setText : function(text){
34646 this.textEl.update(text);
34647 this.setTooltip(text);
34648 //if(!this.tabPanel.resizeTabs){
34649 // this.autoSize();
34653 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34655 activate : function(){
34656 this.tabPanel.activate(this.id);
34660 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34662 disable : function(){
34663 if(this.tabPanel.active != this){
34664 this.disabled = true;
34665 this.pnode.addClass("disabled");
34670 * Enables this TabPanelItem if it was previously disabled.
34672 enable : function(){
34673 this.disabled = false;
34674 this.pnode.removeClass("disabled");
34678 * Sets the content for this TabPanelItem.
34679 * @param {String} content The content
34680 * @param {Boolean} loadScripts true to look for and load scripts
34682 setContent : function(content, loadScripts){
34683 this.bodyEl.update(content, loadScripts);
34687 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34688 * @return {Roo.UpdateManager} The UpdateManager
34690 getUpdateManager : function(){
34691 return this.bodyEl.getUpdateManager();
34695 * Set a URL to be used to load the content for this TabPanelItem.
34696 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34697 * @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)
34698 * @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)
34699 * @return {Roo.UpdateManager} The UpdateManager
34701 setUrl : function(url, params, loadOnce){
34702 if(this.refreshDelegate){
34703 this.un('activate', this.refreshDelegate);
34705 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34706 this.on("activate", this.refreshDelegate);
34707 return this.bodyEl.getUpdateManager();
34711 _handleRefresh : function(url, params, loadOnce){
34712 if(!loadOnce || !this.loaded){
34713 var updater = this.bodyEl.getUpdateManager();
34714 updater.update(url, params, this._setLoaded.createDelegate(this));
34719 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
34720 * Will fail silently if the setUrl method has not been called.
34721 * This does not activate the panel, just updates its content.
34723 refresh : function(){
34724 if(this.refreshDelegate){
34725 this.loaded = false;
34726 this.refreshDelegate();
34731 _setLoaded : function(){
34732 this.loaded = true;
34736 closeClick : function(e){
34739 this.fireEvent("beforeclose", this, o);
34740 if(o.cancel !== true){
34741 this.tabPanel.removeTab(this.id);
34745 * The text displayed in the tooltip for the close icon.
34748 closeText : "Close this tab"