4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
23 * Do not use directly - it does not do anything..
24 * @param {Object} config The config object
29 Roo.bootstrap.Component = function(config){
30 Roo.bootstrap.Component.superclass.constructor.call(this, config);
34 * @event childrenrendered
35 * Fires when the children have been rendered..
36 * @param {Roo.bootstrap.Component} this
38 "childrenrendered" : true
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
50 allowDomMove : false, // to stop relocations in parent onRender...
60 * Initialize Events for the element
62 initEvents : function() { },
68 can_build_overlaid : true,
70 container_method : false,
77 // returns the parent component..
78 return Roo.ComponentMgr.get(this.parentId)
84 onRender : function(ct, position)
86 // Roo.log("Call onRender: " + this.xtype);
88 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
91 if (this.el.attr('xtype')) {
92 this.el.attr('xtypex', this.el.attr('xtype'));
93 this.el.dom.removeAttribute('xtype');
103 var cfg = Roo.apply({}, this.getAutoCreate());
105 cfg.id = this.id || Roo.id();
107 // fill in the extra attributes
108 if (this.xattr && typeof(this.xattr) =='object') {
109 for (var i in this.xattr) {
110 cfg[i] = this.xattr[i];
115 cfg.dataId = this.dataId;
119 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
122 if (this.style) { // fixme needs to support more complex style data.
123 cfg.style = this.style;
127 cfg.name = this.name;
130 this.el = ct.createChild(cfg, position);
133 this.tooltipEl().attr('tooltip', this.tooltip);
136 if(this.tabIndex !== undefined){
137 this.el.dom.setAttribute('tabIndex', this.tabIndex);
144 * Fetch the element to add children to
145 * @return {Roo.Element} defaults to this.el
147 getChildContainer : function()
152 * Fetch the element to display the tooltip on.
153 * @return {Roo.Element} defaults to this.el
155 tooltipEl : function()
160 addxtype : function(tree,cntr)
164 cn = Roo.factory(tree);
165 //Roo.log(['addxtype', cn]);
167 cn.parentType = this.xtype; //??
168 cn.parentId = this.id;
170 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171 if (typeof(cn.container_method) == 'string') {
172 cntr = cn.container_method;
176 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
178 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
180 var build_from_html = Roo.XComponent.build_from_html;
182 var is_body = (tree.xtype == 'Body') ;
184 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186 var self_cntr_el = Roo.get(this[cntr](false));
188 // do not try and build conditional elements
189 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
193 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195 return this.addxtypeChild(tree,cntr, is_body);
198 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
201 return this.addxtypeChild(Roo.apply({}, tree),cntr);
204 Roo.log('skipping render');
210 if (!build_from_html) {
214 // this i think handles overlaying multiple children of the same type
215 // with the sam eelement.. - which might be buggy..
217 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
227 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
234 addxtypeChild : function (tree, cntr, is_body)
236 Roo.debug && Roo.log('addxtypeChild:' + cntr);
238 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
241 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242 (typeof(tree['flexy:foreach']) != 'undefined');
246 skip_children = false;
247 // render the element if it's not BODY.
250 cn = Roo.factory(tree);
252 cn.parentType = this.xtype; //??
253 cn.parentId = this.id;
255 var build_from_html = Roo.XComponent.build_from_html;
258 // does the container contain child eleemnts with 'xtype' attributes.
259 // that match this xtype..
260 // note - when we render we create these as well..
261 // so we should check to see if body has xtype set.
262 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
264 var self_cntr_el = Roo.get(this[cntr](false));
265 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
267 //Roo.log(Roo.XComponent.build_from_html);
268 //Roo.log("got echild:");
271 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272 // and are not displayed -this causes this to use up the wrong element when matching.
273 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
276 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
283 //echild.dom.removeAttribute('xtype');
285 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286 Roo.debug && Roo.log(self_cntr_el);
287 Roo.debug && Roo.log(echild);
288 Roo.debug && Roo.log(cn);
294 // if object has flexy:if - then it may or may not be rendered.
295 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
296 // skip a flexy if element.
297 Roo.debug && Roo.log('skipping render');
298 Roo.debug && Roo.log(tree);
300 Roo.debug && Roo.log('skipping all children');
301 skip_children = true;
306 // actually if flexy:foreach is found, we really want to create
307 // multiple copies here...
309 //Roo.log(this[cntr]());
310 // some elements do not have render methods.. like the layouts...
311 cn.render && cn.render(this[cntr](true));
313 // then add the element..
321 if (typeof (tree.menu) != 'undefined') {
322 tree.menu.parentType = cn.xtype;
323 tree.menu.triggerEl = cn.el;
324 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
328 if (!tree.items || !tree.items.length) {
330 //Roo.log(["no children", this]);
335 var items = tree.items;
338 //Roo.log(items.length);
340 if (!skip_children) {
341 for(var i =0;i < items.length;i++) {
342 // Roo.log(['add child', items[i]]);
343 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
349 //Roo.log("fire childrenrendered");
351 cn.fireEvent('childrenrendered', this);
356 * Show a component - removes 'hidden' class
361 this.el.removeClass('hidden');
365 * Hide a component - adds 'hidden' class
369 if (this.el && !this.el.hasClass('hidden')) {
370 this.el.addClass('hidden');
384 * @class Roo.bootstrap.Body
385 * @extends Roo.bootstrap.Component
386 * Bootstrap Body class
390 * @param {Object} config The config object
393 Roo.bootstrap.Body = function(config){
394 Roo.bootstrap.Body.superclass.constructor.call(this, config);
395 this.el = Roo.get(document.body);
396 if (this.cls && this.cls.length) {
397 Roo.get(document.body).addClass(this.cls);
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
403 is_body : true,// just to make sure it's constructed?
408 onRender : function(ct, position)
410 /* Roo.log("Roo.bootstrap.Body - onRender");
411 if (this.cls && this.cls.length) {
412 Roo.get(document.body).addClass(this.cls);
432 * @class Roo.bootstrap.ButtonGroup
433 * @extends Roo.bootstrap.Component
434 * Bootstrap ButtonGroup class
435 * @cfg {String} size lg | sm | xs (default empty normal)
436 * @cfg {String} align vertical | justified (default none)
437 * @cfg {String} direction up | down (default down)
438 * @cfg {Boolean} toolbar false | true
439 * @cfg {Boolean} btn true | false
444 * @param {Object} config The config object
447 Roo.bootstrap.ButtonGroup = function(config){
448 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
459 getAutoCreate : function(){
465 cfg.html = this.html || cfg.html;
476 if (['vertical','justified'].indexOf(this.align)!==-1) {
477 cfg.cls = 'btn-group-' + this.align;
479 if (this.align == 'justified') {
480 console.log(this.items);
484 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485 cfg.cls += ' btn-group-' + this.size;
488 if (this.direction == 'up') {
489 cfg.cls += ' dropup' ;
505 * @class Roo.bootstrap.Button
506 * @extends Roo.bootstrap.Component
507 * Bootstrap Button class
508 * @cfg {String} html The button content
509 * @cfg {String} weight ( primary | success | info | warning | danger | link ) default
510 * @cfg {String} size ( lg | sm | xs)
511 * @cfg {String} tag ( a | input | submit)
512 * @cfg {String} href empty or href
513 * @cfg {Boolean} disabled default false;
514 * @cfg {Boolean} isClose default false;
515 * @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)
516 * @cfg {String} badge text for badge
517 * @cfg {String} theme default
518 * @cfg {Boolean} inverse
519 * @cfg {Boolean} toggle
520 * @cfg {String} ontext text for on toggle state
521 * @cfg {String} offtext text for off toggle state
522 * @cfg {Boolean} defaulton
523 * @cfg {Boolean} preventDefault default true
524 * @cfg {Boolean} removeClass remove the standard class..
525 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
528 * Create a new button
529 * @param {Object} config The config object
533 Roo.bootstrap.Button = function(config){
534 Roo.bootstrap.Button.superclass.constructor.call(this, config);
539 * When a butotn is pressed
540 * @param {Roo.bootstrap.Button} this
541 * @param {Roo.EventObject} e
546 * After the button has been toggles
547 * @param {Roo.EventObject} e
548 * @param {boolean} pressed (also available as button.pressed)
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
572 preventDefault: true,
581 getAutoCreate : function(){
589 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
595 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
597 if (this.toggle == true) {
600 cls: 'slider-frame roo-button',
605 'data-off-text':'OFF',
606 cls: 'slider-button',
612 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613 cfg.cls += ' '+this.weight;
622 cfg["aria-hidden"] = true;
624 cfg.html = "×";
630 if (this.theme==='default') {
631 cfg.cls = 'btn roo-button';
633 //if (this.parentType != 'Navbar') {
634 this.weight = this.weight.length ? this.weight : 'default';
636 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
638 cfg.cls += ' btn-' + this.weight;
640 } else if (this.theme==='glow') {
643 cfg.cls = 'btn-glow roo-button';
645 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647 cfg.cls += ' ' + this.weight;
653 this.cls += ' inverse';
658 cfg.cls += ' active';
662 cfg.disabled = 'disabled';
666 Roo.log('changing to ul' );
668 this.glyphicon = 'caret';
671 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
673 //gsRoo.log(this.parentType);
674 if (this.parentType === 'Navbar' && !this.parent().bar) {
675 Roo.log('changing to li?');
684 href : this.href || '#'
687 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
688 cfg.cls += ' dropdown';
695 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
697 if (this.glyphicon) {
698 cfg.html = ' ' + cfg.html;
703 cls: 'glyphicon glyphicon-' + this.glyphicon
713 // cfg.cls='btn roo-button';
717 var value = cfg.html;
722 cls: 'glyphicon glyphicon-' + this.glyphicon,
741 cfg.cls += ' dropdown';
742 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745 if (cfg.tag !== 'a' && this.href !== '') {
746 throw "Tag must be a to set href.";
747 } else if (this.href.length > 0) {
748 cfg.href = this.href;
751 if(this.removeClass){
756 cfg.target = this.target;
761 initEvents: function() {
762 // Roo.log('init events?');
763 // Roo.log(this.el.dom);
766 if (typeof (this.menu) != 'undefined') {
767 this.menu.parentType = this.xtype;
768 this.menu.triggerEl = this.el;
769 this.addxtype(Roo.apply({}, this.menu));
773 if (this.el.hasClass('roo-button')) {
774 this.el.on('click', this.onClick, this);
776 this.el.select('.roo-button').on('click', this.onClick, this);
779 if(this.removeClass){
780 this.el.on('click', this.onClick, this);
783 this.el.enableDisplayMode();
786 onClick : function(e)
793 Roo.log('button on click ');
794 if(this.preventDefault){
797 if (this.pressed === true || this.pressed === false) {
798 this.pressed = !this.pressed;
799 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800 this.fireEvent('toggle', this, e, this.pressed);
804 this.fireEvent('click', this, e);
808 * Enables this button
812 this.disabled = false;
813 this.el.removeClass('disabled');
817 * Disable this button
821 this.disabled = true;
822 this.el.addClass('disabled');
825 * sets the active state on/off,
826 * @param {Boolean} state (optional) Force a particular state
828 setActive : function(v) {
830 this.el[v ? 'addClass' : 'removeClass']('active');
833 * toggles the current active state
835 toggleActive : function()
837 var active = this.el.hasClass('active');
838 this.setActive(!active);
842 setText : function(str)
844 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
848 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
871 * @class Roo.bootstrap.Column
872 * @extends Roo.bootstrap.Component
873 * Bootstrap Column class
874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884 * @cfg {Boolean} hidden (true|false) hide the element
885 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886 * @cfg {String} fa (ban|check|...) font awesome icon
887 * @cfg {Number} fasize (1|2|....) font awsome size
889 * @cfg {String} icon (info-sign|check|...) glyphicon name
891 * @cfg {String} html content of column.
894 * Create a new Column
895 * @param {Object} config The config object
898 Roo.bootstrap.Column = function(config){
899 Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
921 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
929 ['xs','sm','md','lg'].map(function(size){
930 //Roo.log( size + ':' + settings[size]);
932 if (settings[size+'off'] !== false) {
933 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936 if (settings[size] === false) {
940 if (!settings[size]) { // 0 = hidden
941 cfg.cls += ' hidden-' + size;
944 cfg.cls += ' col-' + size + '-' + settings[size];
949 cfg.cls += ' hidden';
952 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953 cfg.cls +=' alert alert-' + this.alert;
957 if (this.html.length) {
958 cfg.html = this.html;
962 if (this.fasize > 1) {
963 fasize = ' fa-' + this.fasize + 'x';
965 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
970 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
989 * @class Roo.bootstrap.Container
990 * @extends Roo.bootstrap.Component
991 * Bootstrap Container class
992 * @cfg {Boolean} jumbotron is it a jumbotron element
993 * @cfg {String} html content of element
994 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995 * @cfg {String} panel (primary|success|info|warning|danger) render as panel - type - primary/success.....
996 * @cfg {String} header content of header (for panel)
997 * @cfg {String} footer content of footer (for panel)
998 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999 * @cfg {String} tag (header|aside|section) type of HTML tag.
1000 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001 * @cfg {String} fa font awesome icon
1002 * @cfg {String} icon (info-sign|check|...) glyphicon name
1003 * @cfg {Boolean} hidden (true|false) hide the element
1004 * @cfg {Boolean} expandable (true|false) default false
1005 * @cfg {Boolean} expanded (true|false) default true
1006 * @cfg {String} rheader contet on the right of header
1007 * @cfg {Boolean} clickable (true|false) default false
1011 * Create a new Container
1012 * @param {Object} config The config object
1015 Roo.bootstrap.Container = function(config){
1016 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1022 * After the panel has been expand
1024 * @param {Roo.bootstrap.Container} this
1029 * After the panel has been collapsed
1031 * @param {Roo.bootstrap.Container} this
1036 * When a element is chick
1037 * @param {Roo.bootstrap.Container} this
1038 * @param {Roo.EventObject} e
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1062 getChildContainer : function() {
1068 if (this.panel.length) {
1069 return this.el.select('.panel-body',true).first();
1076 getAutoCreate : function(){
1079 tag : this.tag || 'div',
1083 if (this.jumbotron) {
1084 cfg.cls = 'jumbotron';
1089 // - this is applied by the parent..
1091 // cfg.cls = this.cls + '';
1094 if (this.sticky.length) {
1096 var bd = Roo.get(document.body);
1097 if (!bd.hasClass('bootstrap-sticky')) {
1098 bd.addClass('bootstrap-sticky');
1099 Roo.select('html',true).setStyle('height', '100%');
1102 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1106 if (this.well.length) {
1107 switch (this.well) {
1110 cfg.cls +=' well well-' +this.well;
1119 cfg.cls += ' hidden';
1123 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124 cfg.cls +=' alert alert-' + this.alert;
1129 if (this.panel.length) {
1130 cfg.cls += ' panel panel-' + this.panel;
1132 if (this.header.length) {
1136 if(this.expandable){
1138 cfg.cls = cfg.cls + ' expandable';
1142 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1150 cls : 'panel-title',
1151 html : (this.expandable ? ' ' : '') + this.header
1155 cls: 'panel-header-right',
1161 cls : 'panel-heading',
1162 style : this.expandable ? 'cursor: pointer' : '',
1170 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1175 if (this.footer.length) {
1177 cls : 'panel-footer',
1186 body.html = this.html || cfg.html;
1187 // prefix with the icons..
1189 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1197 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198 cfg.cls = 'container';
1204 initEvents: function()
1206 if(this.expandable){
1207 var headerEl = this.headerEl();
1210 headerEl.on('click', this.onToggleClick, this);
1215 this.el.on('click', this.onClick, this);
1220 onToggleClick : function()
1222 var headerEl = this.headerEl();
1238 if(this.fireEvent('expand', this)) {
1240 this.expanded = true;
1242 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1244 this.el.select('.panel-body',true).first().removeClass('hide');
1246 var toggleEl = this.toggleEl();
1252 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1257 collapse : function()
1259 if(this.fireEvent('collapse', this)) {
1261 this.expanded = false;
1263 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264 this.el.select('.panel-body',true).first().addClass('hide');
1266 var toggleEl = this.toggleEl();
1272 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1276 toggleEl : function()
1278 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1282 return this.el.select('.panel-heading .fa',true).first();
1285 headerEl : function()
1287 if(!this.el || !this.panel.length || !this.header.length){
1291 return this.el.select('.panel-heading',true).first()
1294 titleEl : function()
1296 if(!this.el || !this.panel.length || !this.header.length){
1300 return this.el.select('.panel-title',true).first();
1303 setTitle : function(v)
1305 var titleEl = this.titleEl();
1311 titleEl.dom.innerHTML = v;
1314 getTitle : function()
1317 var titleEl = this.titleEl();
1323 return titleEl.dom.innerHTML;
1326 setRightTitle : function(v)
1328 var t = this.el.select('.panel-header-right',true).first();
1334 t.dom.innerHTML = v;
1337 onClick : function(e)
1341 this.fireEvent('click', this, e);
1355 * @class Roo.bootstrap.Img
1356 * @extends Roo.bootstrap.Component
1357 * Bootstrap Img class
1358 * @cfg {Boolean} imgResponsive false | true
1359 * @cfg {String} border rounded | circle | thumbnail
1360 * @cfg {String} src image source
1361 * @cfg {String} alt image alternative text
1362 * @cfg {String} href a tag href
1363 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364 * @cfg {String} xsUrl xs image source
1365 * @cfg {String} smUrl sm image source
1366 * @cfg {String} mdUrl md image source
1367 * @cfg {String} lgUrl lg image source
1370 * Create a new Input
1371 * @param {Object} config The config object
1374 Roo.bootstrap.Img = function(config){
1375 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1381 * The img click event for the img.
1382 * @param {Roo.EventObject} e
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1390 imgResponsive: true,
1400 getAutoCreate : function()
1402 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403 return this.createSingleImg();
1408 cls: 'roo-image-responsive-group',
1413 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1415 if(!_this[size + 'Url']){
1421 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422 html: _this.html || cfg.html,
1423 src: _this[size + 'Url']
1426 img.cls += ' roo-image-responsive-' + size;
1428 var s = ['xs', 'sm', 'md', 'lg'];
1430 s.splice(s.indexOf(size), 1);
1432 Roo.each(s, function(ss){
1433 img.cls += ' hidden-' + ss;
1436 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437 cfg.cls += ' img-' + _this.border;
1441 cfg.alt = _this.alt;
1454 a.target = _this.target;
1458 cfg.cn.push((_this.href) ? a : img);
1465 createSingleImg : function()
1469 cls: (this.imgResponsive) ? 'img-responsive' : '',
1471 src : 'about:blank' // just incase src get's set to undefined?!?
1474 cfg.html = this.html || cfg.html;
1476 cfg.src = this.src || cfg.src;
1478 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479 cfg.cls += ' img-' + this.border;
1496 a.target = this.target;
1501 return (this.href) ? a : cfg;
1504 initEvents: function()
1507 this.el.on('click', this.onClick, this);
1512 onClick : function(e)
1514 Roo.log('img onclick');
1515 this.fireEvent('click', this, e);
1529 * @class Roo.bootstrap.Link
1530 * @extends Roo.bootstrap.Component
1531 * Bootstrap Link Class
1532 * @cfg {String} alt image alternative text
1533 * @cfg {String} href a tag href
1534 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535 * @cfg {String} html the content of the link.
1536 * @cfg {String} anchor name for the anchor link
1537 * @cfg {String} fa - favicon
1539 * @cfg {Boolean} preventDefault (true | false) default false
1543 * Create a new Input
1544 * @param {Object} config The config object
1547 Roo.bootstrap.Link = function(config){
1548 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1554 * The img click event for the img.
1555 * @param {Roo.EventObject} e
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1565 preventDefault: false,
1571 getAutoCreate : function()
1573 var html = this.html || '';
1575 if (this.fa !== false) {
1576 html = '<i class="fa fa-' + this.fa + '"></i>';
1581 // anchor's do not require html/href...
1582 if (this.anchor === false) {
1584 cfg.href = this.href || '#';
1586 cfg.name = this.anchor;
1587 if (this.html !== false || this.fa !== false) {
1590 if (this.href !== false) {
1591 cfg.href = this.href;
1595 if(this.alt !== false){
1600 if(this.target !== false) {
1601 cfg.target = this.target;
1607 initEvents: function() {
1609 if(!this.href || this.preventDefault){
1610 this.el.on('click', this.onClick, this);
1614 onClick : function(e)
1616 if(this.preventDefault){
1619 //Roo.log('img onclick');
1620 this.fireEvent('click', this, e);
1633 * @class Roo.bootstrap.Header
1634 * @extends Roo.bootstrap.Component
1635 * Bootstrap Header class
1636 * @cfg {String} html content of header
1637 * @cfg {Number} level (1|2|3|4|5|6) default 1
1640 * Create a new Header
1641 * @param {Object} config The config object
1645 Roo.bootstrap.Header = function(config){
1646 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1657 getAutoCreate : function(){
1662 tag: 'h' + (1 *this.level),
1663 html: this.html || ''
1675 * Ext JS Library 1.1.1
1676 * Copyright(c) 2006-2007, Ext JS, LLC.
1678 * Originally Released Under LGPL - original licence link has changed is not relivant.
1681 * <script type="text/javascript">
1685 * @class Roo.bootstrap.MenuMgr
1686 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1689 Roo.bootstrap.MenuMgr = function(){
1690 var menus, active, groups = {}, attached = false, lastShow = new Date();
1692 // private - called when first menu is created
1695 active = new Roo.util.MixedCollection();
1696 Roo.get(document).addKeyListener(27, function(){
1697 if(active.length > 0){
1705 if(active && active.length > 0){
1706 var c = active.clone();
1716 if(active.length < 1){
1717 Roo.get(document).un("mouseup", onMouseDown);
1725 var last = active.last();
1726 lastShow = new Date();
1729 Roo.get(document).on("mouseup", onMouseDown);
1734 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735 m.parentMenu.activeChild = m;
1736 }else if(last && last.isVisible()){
1737 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1742 function onBeforeHide(m){
1744 m.activeChild.hide();
1746 if(m.autoHideTimer){
1747 clearTimeout(m.autoHideTimer);
1748 delete m.autoHideTimer;
1753 function onBeforeShow(m){
1754 var pm = m.parentMenu;
1755 if(!pm && !m.allowOtherMenus){
1757 }else if(pm && pm.activeChild && active != m){
1758 pm.activeChild.hide();
1762 // private this should really trigger on mouseup..
1763 function onMouseDown(e){
1764 Roo.log("on Mouse Up");
1766 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767 Roo.log("MenuManager hideAll");
1776 function onBeforeCheck(mi, state){
1778 var g = groups[mi.group];
1779 for(var i = 0, l = g.length; i < l; i++){
1781 g[i].setChecked(false);
1790 * Hides all menus that are currently visible
1792 hideAll : function(){
1797 register : function(menu){
1801 menus[menu.id] = menu;
1802 menu.on("beforehide", onBeforeHide);
1803 menu.on("hide", onHide);
1804 menu.on("beforeshow", onBeforeShow);
1805 menu.on("show", onShow);
1807 if(g && menu.events["checkchange"]){
1811 groups[g].push(menu);
1812 menu.on("checkchange", onCheck);
1817 * Returns a {@link Roo.menu.Menu} object
1818 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819 * be used to generate and return a new Menu instance.
1821 get : function(menu){
1822 if(typeof menu == "string"){ // menu id
1824 }else if(menu.events){ // menu instance
1827 /*else if(typeof menu.length == 'number'){ // array of menu items?
1828 return new Roo.bootstrap.Menu({items:menu});
1829 }else{ // otherwise, must be a config
1830 return new Roo.bootstrap.Menu(menu);
1837 unregister : function(menu){
1838 delete menus[menu.id];
1839 menu.un("beforehide", onBeforeHide);
1840 menu.un("hide", onHide);
1841 menu.un("beforeshow", onBeforeShow);
1842 menu.un("show", onShow);
1844 if(g && menu.events["checkchange"]){
1845 groups[g].remove(menu);
1846 menu.un("checkchange", onCheck);
1851 registerCheckable : function(menuItem){
1852 var g = menuItem.group;
1857 groups[g].push(menuItem);
1858 menuItem.on("beforecheckchange", onBeforeCheck);
1863 unregisterCheckable : function(menuItem){
1864 var g = menuItem.group;
1866 groups[g].remove(menuItem);
1867 menuItem.un("beforecheckchange", onBeforeCheck);
1879 * @class Roo.bootstrap.Menu
1880 * @extends Roo.bootstrap.Component
1881 * Bootstrap Menu class - container for MenuItems
1882 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883 * @cfg {bool} hidden if the menu should be hidden when rendered.
1884 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1885 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1889 * @param {Object} config The config object
1893 Roo.bootstrap.Menu = function(config){
1894 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895 if (this.registerMenu && this.type != 'treeview') {
1896 Roo.bootstrap.MenuMgr.register(this);
1901 * Fires before this menu is displayed
1902 * @param {Roo.menu.Menu} this
1907 * Fires before this menu is hidden
1908 * @param {Roo.menu.Menu} this
1913 * Fires after this menu is displayed
1914 * @param {Roo.menu.Menu} this
1919 * Fires after this menu is hidden
1920 * @param {Roo.menu.Menu} this
1925 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926 * @param {Roo.menu.Menu} this
1927 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928 * @param {Roo.EventObject} e
1933 * Fires when the mouse is hovering over this menu
1934 * @param {Roo.menu.Menu} this
1935 * @param {Roo.EventObject} e
1936 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1941 * Fires when the mouse exits this menu
1942 * @param {Roo.menu.Menu} this
1943 * @param {Roo.EventObject} e
1944 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1949 * Fires when a menu item contained in this menu is clicked
1950 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951 * @param {Roo.EventObject} e
1955 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1962 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1965 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1967 registerMenu : true,
1969 menuItems :false, // stores the menu items..
1979 getChildContainer : function() {
1983 getAutoCreate : function(){
1985 //if (['right'].indexOf(this.align)!==-1) {
1986 // cfg.cn[1].cls += ' pull-right'
1992 cls : 'dropdown-menu' ,
1993 style : 'z-index:1000'
1997 if (this.type === 'submenu') {
1998 cfg.cls = 'submenu active';
2000 if (this.type === 'treeview') {
2001 cfg.cls = 'treeview-menu';
2006 initEvents : function() {
2008 // Roo.log("ADD event");
2009 // Roo.log(this.triggerEl.dom);
2011 this.triggerEl.on('click', this.onTriggerClick, this);
2013 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2015 this.triggerEl.addClass('dropdown-toggle');
2018 this.el.on('touchstart' , this.onTouch, this);
2020 this.el.on('click' , this.onClick, this);
2022 this.el.on("mouseover", this.onMouseOver, this);
2023 this.el.on("mouseout", this.onMouseOut, this);
2027 findTargetItem : function(e)
2029 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2033 //Roo.log(t); Roo.log(t.id);
2035 //Roo.log(this.menuitems);
2036 return this.menuitems.get(t.id);
2038 //return this.items.get(t.menuItemId);
2044 onTouch : function(e)
2046 Roo.log("menu.onTouch");
2047 //e.stopEvent(); this make the user popdown broken
2051 onClick : function(e)
2053 Roo.log("menu.onClick");
2055 var t = this.findTargetItem(e);
2056 if(!t || t.isContainer){
2061 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2062 if(t == this.activeItem && t.shouldDeactivate(e)){
2063 this.activeItem.deactivate();
2064 delete this.activeItem;
2068 this.setActiveItem(t, true);
2076 Roo.log('pass click event');
2080 this.fireEvent("click", this, t, e);
2084 (function() { _this.hide(); }).defer(500);
2087 onMouseOver : function(e){
2088 var t = this.findTargetItem(e);
2091 // if(t.canActivate && !t.disabled){
2092 // this.setActiveItem(t, true);
2096 this.fireEvent("mouseover", this, e, t);
2098 isVisible : function(){
2099 return !this.hidden;
2101 onMouseOut : function(e){
2102 var t = this.findTargetItem(e);
2105 // if(t == this.activeItem && t.shouldDeactivate(e)){
2106 // this.activeItem.deactivate();
2107 // delete this.activeItem;
2110 this.fireEvent("mouseout", this, e, t);
2115 * Displays this menu relative to another element
2116 * @param {String/HTMLElement/Roo.Element} element The element to align to
2117 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118 * the element (defaults to this.defaultAlign)
2119 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2121 show : function(el, pos, parentMenu){
2122 this.parentMenu = parentMenu;
2126 this.fireEvent("beforeshow", this);
2127 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2130 * Displays this menu at a specific xy position
2131 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134 showAt : function(xy, parentMenu, /* private: */_e){
2135 this.parentMenu = parentMenu;
2140 this.fireEvent("beforeshow", this);
2141 //xy = this.el.adjustForConstraints(xy);
2145 this.hideMenuItems();
2146 this.hidden = false;
2147 this.triggerEl.addClass('open');
2149 if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2153 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2158 this.fireEvent("show", this);
2164 this.doFocus.defer(50, this);
2168 doFocus : function(){
2170 this.focusEl.focus();
2175 * Hides this menu and optionally all parent menus
2176 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2178 hide : function(deep)
2181 this.hideMenuItems();
2182 if(this.el && this.isVisible()){
2183 this.fireEvent("beforehide", this);
2184 if(this.activeItem){
2185 this.activeItem.deactivate();
2186 this.activeItem = null;
2188 this.triggerEl.removeClass('open');;
2190 this.fireEvent("hide", this);
2192 if(deep === true && this.parentMenu){
2193 this.parentMenu.hide(true);
2197 onTriggerClick : function(e)
2199 Roo.log('trigger click');
2201 var target = e.getTarget();
2203 Roo.log(target.nodeName.toLowerCase());
2205 if(target.nodeName.toLowerCase() === 'i'){
2211 onTriggerPress : function(e)
2213 Roo.log('trigger press');
2214 //Roo.log(e.getTarget());
2215 // Roo.log(this.triggerEl.dom);
2217 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2218 var pel = Roo.get(e.getTarget());
2219 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2220 Roo.log('is treeview or dropdown?');
2224 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2228 if (this.isVisible()) {
2233 this.show(this.triggerEl, false, false);
2236 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2243 hideMenuItems : function()
2245 Roo.log("hide Menu Items");
2249 //$(backdrop).remove()
2250 this.el.select('.open',true).each(function(aa) {
2252 aa.removeClass('open');
2253 //var parent = getParent($(this))
2254 //var relatedTarget = { relatedTarget: this }
2256 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2257 //if (e.isDefaultPrevented()) return
2258 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2261 addxtypeChild : function (tree, cntr) {
2262 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2264 this.menuitems.add(comp);
2285 * @class Roo.bootstrap.MenuItem
2286 * @extends Roo.bootstrap.Component
2287 * Bootstrap MenuItem class
2288 * @cfg {String} html the menu label
2289 * @cfg {String} href the link
2290 * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2291 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2292 * @cfg {Boolean} active used on sidebars to highlight active itesm
2293 * @cfg {String} fa favicon to show on left of menu item.
2294 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2298 * Create a new MenuItem
2299 * @param {Object} config The config object
2303 Roo.bootstrap.MenuItem = function(config){
2304 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2309 * The raw click event for the entire grid.
2310 * @param {Roo.bootstrap.MenuItem} this
2311 * @param {Roo.EventObject} e
2317 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2321 preventDefault: true,
2322 isContainer : false,
2326 getAutoCreate : function(){
2328 if(this.isContainer){
2331 cls: 'dropdown-menu-item'
2345 if (this.fa !== false) {
2348 cls : 'fa fa-' + this.fa
2357 cls: 'dropdown-menu-item',
2360 if (this.parent().type == 'treeview') {
2361 cfg.cls = 'treeview-menu';
2364 cfg.cls += ' active';
2369 anc.href = this.href || cfg.cn[0].href ;
2370 ctag.html = this.html || cfg.cn[0].html ;
2374 initEvents: function()
2376 if (this.parent().type == 'treeview') {
2377 this.el.select('a').on('click', this.onClick, this);
2380 this.menu.parentType = this.xtype;
2381 this.menu.triggerEl = this.el;
2382 this.menu = this.addxtype(Roo.apply({}, this.menu));
2386 onClick : function(e)
2388 Roo.log('item on click ');
2389 //if(this.preventDefault){
2390 // e.preventDefault();
2392 //this.parent().hideMenuItems();
2394 this.fireEvent('click', this, e);
2413 * @class Roo.bootstrap.MenuSeparator
2414 * @extends Roo.bootstrap.Component
2415 * Bootstrap MenuSeparator class
2418 * Create a new MenuItem
2419 * @param {Object} config The config object
2423 Roo.bootstrap.MenuSeparator = function(config){
2424 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2427 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2429 getAutoCreate : function(){
2448 * @class Roo.bootstrap.Modal
2449 * @extends Roo.bootstrap.Component
2450 * Bootstrap Modal class
2451 * @cfg {String} title Title of dialog
2452 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2453 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2454 * @cfg {Boolean} specificTitle default false
2455 * @cfg {Array} buttons Array of buttons or standard button set..
2456 * @cfg {String} buttonPosition (left|right|center) default right
2457 * @cfg {Boolean} animate default true
2458 * @cfg {Boolean} allow_close default true
2459 * @cfg {Boolean} fitwindow default true
2460 * @cfg {String} size (sm|lg) default empty
2464 * Create a new Modal Dialog
2465 * @param {Object} config The config object
2468 Roo.bootstrap.Modal = function(config){
2469 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2474 * The raw btnclick event for the button
2475 * @param {Roo.EventObject} e
2479 this.buttons = this.buttons || [];
2482 this.tmpl = Roo.factory(this.tmpl);
2487 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2489 title : 'test dialog',
2499 specificTitle: false,
2501 buttonPosition: 'right',
2520 onRender : function(ct, position)
2522 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2525 var cfg = Roo.apply({}, this.getAutoCreate());
2528 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2530 //if (!cfg.name.length) {
2534 cfg.cls += ' ' + this.cls;
2537 cfg.style = this.style;
2539 this.el = Roo.get(document.body).createChild(cfg, position);
2541 //var type = this.el.dom.type;
2544 if(this.tabIndex !== undefined){
2545 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2548 this.dialogEl = this.el.select('.modal-dialog',true).first();
2549 this.bodyEl = this.el.select('.modal-body',true).first();
2550 this.closeEl = this.el.select('.modal-header .close', true).first();
2551 this.footerEl = this.el.select('.modal-footer',true).first();
2552 this.titleEl = this.el.select('.modal-title',true).first();
2556 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2557 this.maskEl.enableDisplayMode("block");
2559 //this.el.addClass("x-dlg-modal");
2561 if (this.buttons.length) {
2562 Roo.each(this.buttons, function(bb) {
2563 var b = Roo.apply({}, bb);
2564 b.xns = b.xns || Roo.bootstrap;
2565 b.xtype = b.xtype || 'Button';
2566 if (typeof(b.listeners) == 'undefined') {
2567 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2570 var btn = Roo.factory(b);
2572 btn.render(this.el.select('.modal-footer div').first());
2576 // render the children.
2579 if(typeof(this.items) != 'undefined'){
2580 var items = this.items;
2583 for(var i =0;i < items.length;i++) {
2584 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2588 this.items = nitems;
2590 // where are these used - they used to be body/close/footer
2594 //this.el.addClass([this.fieldClass, this.cls]);
2598 getAutoCreate : function(){
2603 html : this.html || ''
2608 cls : 'modal-title',
2612 if(this.specificTitle){
2618 if (this.allow_close) {
2630 if(this.size.length){
2631 size = 'modal-' + this.size;
2636 style : 'display: none',
2639 cls: "modal-dialog " + size,
2642 cls : "modal-content",
2645 cls : 'modal-header',
2650 cls : 'modal-footer',
2654 cls: 'btn-' + this.buttonPosition
2671 modal.cls += ' fade';
2677 getChildContainer : function() {
2682 getButtonContainer : function() {
2683 return this.el.select('.modal-footer div',true).first();
2686 initEvents : function()
2688 if (this.allow_close) {
2689 this.closeEl.on('click', this.hide, this);
2691 Roo.EventManager.onWindowResize(this.resize, this, true);
2698 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2699 if (this.fitwindow) {
2700 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2701 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2706 setSize : function(w,h)
2716 if (!this.rendered) {
2720 this.el.setStyle('display', 'block');
2722 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2725 this.el.addClass('in');
2728 this.el.addClass('in');
2732 // not sure how we can show data in here..
2734 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2737 Roo.get(document.body).addClass("x-body-masked");
2738 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2740 this.el.setStyle('zIndex', '10001');
2742 this.fireEvent('show', this);
2743 this.items.forEach(function(e) {
2744 e.layout ? e.layout() : false;
2755 Roo.get(document.body).removeClass("x-body-masked");
2756 this.el.removeClass('in');
2757 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2759 if(this.animate){ // why
2761 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2763 this.el.setStyle('display', 'none');
2766 this.fireEvent('hide', this);
2769 addButton : function(str, cb)
2773 var b = Roo.apply({}, { html : str } );
2774 b.xns = b.xns || Roo.bootstrap;
2775 b.xtype = b.xtype || 'Button';
2776 if (typeof(b.listeners) == 'undefined') {
2777 b.listeners = { click : cb.createDelegate(this) };
2780 var btn = Roo.factory(b);
2782 btn.render(this.el.select('.modal-footer div').first());
2788 setDefaultButton : function(btn)
2790 //this.el.select('.modal-footer').()
2794 resizeTo: function(w,h)
2798 this.dialogEl.setWidth(w);
2799 if (this.diff === false) {
2800 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2803 this.bodyEl.setHeight(h-this.diff);
2807 setContentSize : function(w, h)
2811 onButtonClick: function(btn,e)
2814 this.fireEvent('btnclick', btn.name, e);
2817 * Set the title of the Dialog
2818 * @param {String} str new Title
2820 setTitle: function(str) {
2821 this.titleEl.dom.innerHTML = str;
2824 * Set the body of the Dialog
2825 * @param {String} str new Title
2827 setBody: function(str) {
2828 this.bodyEl.dom.innerHTML = str;
2831 * Set the body of the Dialog using the template
2832 * @param {Obj} data - apply this data to the template and replace the body contents.
2834 applyBody: function(obj)
2837 Roo.log("Error - using apply Body without a template");
2840 this.tmpl.overwrite(this.bodyEl, obj);
2846 Roo.apply(Roo.bootstrap.Modal, {
2848 * Button config that displays a single OK button
2857 * Button config that displays Yes and No buttons
2873 * Button config that displays OK and Cancel buttons
2888 * Button config that displays Yes, No and Cancel buttons
2911 * messagebox - can be used as a replace
2915 * @class Roo.MessageBox
2916 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2920 Roo.Msg.alert('Status', 'Changes saved successfully.');
2922 // Prompt for user data:
2923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2925 // process text value...
2929 // Show a dialog using config options:
2931 title:'Save Changes?',
2932 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2933 buttons: Roo.Msg.YESNOCANCEL,
2940 Roo.bootstrap.MessageBox = function(){
2941 var dlg, opt, mask, waitTimer;
2942 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2943 var buttons, activeTextEl, bwidth;
2947 var handleButton = function(button){
2949 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2953 var handleHide = function(){
2955 dlg.el.removeClass(opt.cls);
2958 // Roo.TaskMgr.stop(waitTimer);
2959 // waitTimer = null;
2964 var updateButtons = function(b){
2967 buttons["ok"].hide();
2968 buttons["cancel"].hide();
2969 buttons["yes"].hide();
2970 buttons["no"].hide();
2971 //dlg.footer.dom.style.display = 'none';
2974 dlg.footerEl.dom.style.display = '';
2975 for(var k in buttons){
2976 if(typeof buttons[k] != "function"){
2979 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2980 width += buttons[k].el.getWidth()+15;
2990 var handleEsc = function(d, k, e){
2991 if(opt && opt.closable !== false){
3001 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3002 * @return {Roo.BasicDialog} The BasicDialog element
3004 getDialog : function(){
3006 dlg = new Roo.bootstrap.Modal( {
3009 //constraintoviewport:false,
3011 //collapsible : false,
3016 //buttonAlign:"center",
3017 closeClick : function(){
3018 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3021 handleButton("cancel");
3026 dlg.on("hide", handleHide);
3028 //dlg.addKeyListener(27, handleEsc);
3030 this.buttons = buttons;
3031 var bt = this.buttonText;
3032 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3033 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3034 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3035 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3037 bodyEl = dlg.bodyEl.createChild({
3039 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3040 '<textarea class="roo-mb-textarea"></textarea>' +
3041 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3043 msgEl = bodyEl.dom.firstChild;
3044 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3045 textboxEl.enableDisplayMode();
3046 textboxEl.addKeyListener([10,13], function(){
3047 if(dlg.isVisible() && opt && opt.buttons){
3050 }else if(opt.buttons.yes){
3051 handleButton("yes");
3055 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3056 textareaEl.enableDisplayMode();
3057 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3058 progressEl.enableDisplayMode();
3059 var pf = progressEl.dom.firstChild;
3061 pp = Roo.get(pf.firstChild);
3062 pp.setHeight(pf.offsetHeight);
3070 * Updates the message box body text
3071 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3072 * the XHTML-compliant non-breaking space character '&#160;')
3073 * @return {Roo.MessageBox} This message box
3075 updateText : function(text){
3076 if(!dlg.isVisible() && !opt.width){
3077 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3079 msgEl.innerHTML = text || ' ';
3081 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3082 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3084 Math.min(opt.width || cw , this.maxWidth),
3085 Math.max(opt.minWidth || this.minWidth, bwidth)
3088 activeTextEl.setWidth(w);
3090 if(dlg.isVisible()){
3091 dlg.fixedcenter = false;
3093 // to big, make it scroll. = But as usual stupid IE does not support
3096 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3097 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3098 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3100 bodyEl.dom.style.height = '';
3101 bodyEl.dom.style.overflowY = '';
3104 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3106 bodyEl.dom.style.overflowX = '';
3109 dlg.setContentSize(w, bodyEl.getHeight());
3110 if(dlg.isVisible()){
3111 dlg.fixedcenter = true;
3117 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3118 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3119 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3120 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3121 * @return {Roo.MessageBox} This message box
3123 updateProgress : function(value, text){
3125 this.updateText(text);
3127 if (pp) { // weird bug on my firefox - for some reason this is not defined
3128 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3134 * Returns true if the message box is currently displayed
3135 * @return {Boolean} True if the message box is visible, else false
3137 isVisible : function(){
3138 return dlg && dlg.isVisible();
3142 * Hides the message box if it is displayed
3145 if(this.isVisible()){
3151 * Displays a new message box, or reinitializes an existing message box, based on the config options
3152 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3153 * The following config object properties are supported:
3155 Property Type Description
3156 ---------- --------------- ------------------------------------------------------------------------------------
3157 animEl String/Element An id or Element from which the message box should animate as it opens and
3158 closes (defaults to undefined)
3159 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3160 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3161 closable Boolean False to hide the top-right close button (defaults to true). Note that
3162 progress and wait dialogs will ignore this property and always hide the
3163 close button as they can only be closed programmatically.
3164 cls String A custom CSS class to apply to the message box element
3165 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3166 displayed (defaults to 75)
3167 fn Function A callback function to execute after closing the dialog. The arguments to the
3168 function will be btn (the name of the button that was clicked, if applicable,
3169 e.g. "ok"), and text (the value of the active text field, if applicable).
3170 Progress and wait dialogs will ignore this option since they do not respond to
3171 user actions and can only be closed programmatically, so any required function
3172 should be called by the same code after it closes the dialog.
3173 icon String A CSS class that provides a background image to be used as an icon for
3174 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3175 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3176 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3177 modal Boolean False to allow user interaction with the page while the message box is
3178 displayed (defaults to true)
3179 msg String A string that will replace the existing message box body text (defaults
3180 to the XHTML-compliant non-breaking space character ' ')
3181 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3182 progress Boolean True to display a progress bar (defaults to false)
3183 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3184 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3185 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3186 title String The title text
3187 value String The string value to set into the active textbox element if displayed
3188 wait Boolean True to display a progress bar (defaults to false)
3189 width Number The width of the dialog in pixels
3196 msg: 'Please enter your address:',
3198 buttons: Roo.MessageBox.OKCANCEL,
3201 animEl: 'addAddressBtn'
3204 * @param {Object} config Configuration options
3205 * @return {Roo.MessageBox} This message box
3207 show : function(options)
3210 // this causes nightmares if you show one dialog after another
3211 // especially on callbacks..
3213 if(this.isVisible()){
3216 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3217 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3218 Roo.log("New Dialog Message:" + options.msg )
3219 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3220 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3223 var d = this.getDialog();
3225 d.setTitle(opt.title || " ");
3226 d.closeEl.setDisplayed(opt.closable !== false);
3227 activeTextEl = textboxEl;
3228 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3233 textareaEl.setHeight(typeof opt.multiline == "number" ?
3234 opt.multiline : this.defaultTextHeight);
3235 activeTextEl = textareaEl;
3244 progressEl.setDisplayed(opt.progress === true);
3245 this.updateProgress(0);
3246 activeTextEl.dom.value = opt.value || "";
3248 dlg.setDefaultButton(activeTextEl);
3250 var bs = opt.buttons;
3254 }else if(bs && bs.yes){
3255 db = buttons["yes"];
3257 dlg.setDefaultButton(db);
3259 bwidth = updateButtons(opt.buttons);
3260 this.updateText(opt.msg);
3262 d.el.addClass(opt.cls);
3264 d.proxyDrag = opt.proxyDrag === true;
3265 d.modal = opt.modal !== false;
3266 d.mask = opt.modal !== false ? mask : false;
3268 // force it to the end of the z-index stack so it gets a cursor in FF
3269 document.body.appendChild(dlg.el.dom);
3270 d.animateTarget = null;
3271 d.show(options.animEl);
3277 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3278 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3279 * and closing the message box when the process is complete.
3280 * @param {String} title The title bar text
3281 * @param {String} msg The message box body text
3282 * @return {Roo.MessageBox} This message box
3284 progress : function(title, msg){
3291 minWidth: this.minProgressWidth,
3298 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3299 * If a callback function is passed it will be called after the user clicks the button, and the
3300 * id of the button that was clicked will be passed as the only parameter to the callback
3301 * (could also be the top-right close button).
3302 * @param {String} title The title bar text
3303 * @param {String} msg The message box body text
3304 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3305 * @param {Object} scope (optional) The scope of the callback function
3306 * @return {Roo.MessageBox} This message box
3308 alert : function(title, msg, fn, scope){
3321 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3322 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3323 * You are responsible for closing the message box when the process is complete.
3324 * @param {String} msg The message box body text
3325 * @param {String} title (optional) The title bar text
3326 * @return {Roo.MessageBox} This message box
3328 wait : function(msg, title){
3339 waitTimer = Roo.TaskMgr.start({
3341 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3349 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3350 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3351 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3352 * @param {String} title The title bar text
3353 * @param {String} msg The message box body text
3354 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3355 * @param {Object} scope (optional) The scope of the callback function
3356 * @return {Roo.MessageBox} This message box
3358 confirm : function(title, msg, fn, scope){
3362 buttons: this.YESNO,
3371 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3372 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3373 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3374 * (could also be the top-right close button) and the text that was entered will be passed as the two
3375 * parameters to the callback.
3376 * @param {String} title The title bar text
3377 * @param {String} msg The message box body text
3378 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3379 * @param {Object} scope (optional) The scope of the callback function
3380 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3381 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3382 * @return {Roo.MessageBox} This message box
3384 prompt : function(title, msg, fn, scope, multiline){
3388 buttons: this.OKCANCEL,
3393 multiline: multiline,
3400 * Button config that displays a single OK button
3405 * Button config that displays Yes and No buttons
3408 YESNO : {yes:true, no:true},
3410 * Button config that displays OK and Cancel buttons
3413 OKCANCEL : {ok:true, cancel:true},
3415 * Button config that displays Yes, No and Cancel buttons
3418 YESNOCANCEL : {yes:true, no:true, cancel:true},
3421 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3424 defaultTextHeight : 75,
3426 * The maximum width in pixels of the message box (defaults to 600)
3431 * The minimum width in pixels of the message box (defaults to 100)
3436 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3437 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3440 minProgressWidth : 250,
3442 * An object containing the default button text strings that can be overriden for localized language support.
3443 * Supported properties are: ok, cancel, yes and no.
3444 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3457 * Shorthand for {@link Roo.MessageBox}
3459 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3460 Roo.Msg = Roo.Msg || Roo.MessageBox;
3469 * @class Roo.bootstrap.Navbar
3470 * @extends Roo.bootstrap.Component
3471 * Bootstrap Navbar class
3474 * Create a new Navbar
3475 * @param {Object} config The config object
3479 Roo.bootstrap.Navbar = function(config){
3480 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3484 * @event beforetoggle
3485 * Fire before toggle the menu
3486 * @param {Roo.EventObject} e
3488 "beforetoggle" : true
3492 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3501 getAutoCreate : function(){
3504 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3508 initEvents :function ()
3510 //Roo.log(this.el.select('.navbar-toggle',true));
3511 this.el.select('.navbar-toggle',true).on('click', function() {
3512 if(this.fireEvent('beforetoggle', this) !== false){
3513 this.el.select('.navbar-collapse',true).toggleClass('in');
3523 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3525 var size = this.el.getSize();
3526 this.maskEl.setSize(size.width, size.height);
3527 this.maskEl.enableDisplayMode("block");
3536 getChildContainer : function()
3538 if (this.el.select('.collapse').getCount()) {
3539 return this.el.select('.collapse',true).first();
3572 * @class Roo.bootstrap.NavSimplebar
3573 * @extends Roo.bootstrap.Navbar
3574 * Bootstrap Sidebar class
3576 * @cfg {Boolean} inverse is inverted color
3578 * @cfg {String} type (nav | pills | tabs)
3579 * @cfg {Boolean} arrangement stacked | justified
3580 * @cfg {String} align (left | right) alignment
3582 * @cfg {Boolean} main (true|false) main nav bar? default false
3583 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3585 * @cfg {String} tag (header|footer|nav|div) default is nav
3591 * Create a new Sidebar
3592 * @param {Object} config The config object
3596 Roo.bootstrap.NavSimplebar = function(config){
3597 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3600 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3616 getAutoCreate : function(){
3620 tag : this.tag || 'div',
3633 this.type = this.type || 'nav';
3634 if (['tabs','pills'].indexOf(this.type)!==-1) {
3635 cfg.cn[0].cls += ' nav-' + this.type
3639 if (this.type!=='nav') {
3640 Roo.log('nav type must be nav/tabs/pills')
3642 cfg.cn[0].cls += ' navbar-nav'
3648 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3649 cfg.cn[0].cls += ' nav-' + this.arrangement;
3653 if (this.align === 'right') {
3654 cfg.cn[0].cls += ' navbar-right';
3658 cfg.cls += ' navbar-inverse';
3685 * @class Roo.bootstrap.NavHeaderbar
3686 * @extends Roo.bootstrap.NavSimplebar
3687 * Bootstrap Sidebar class
3689 * @cfg {String} brand what is brand
3690 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3691 * @cfg {String} brand_href href of the brand
3692 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3693 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3694 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3695 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3698 * Create a new Sidebar
3699 * @param {Object} config The config object
3703 Roo.bootstrap.NavHeaderbar = function(config){
3704 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3708 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3715 desktopCenter : false,
3718 getAutoCreate : function(){
3721 tag: this.nav || 'nav',
3728 if (this.desktopCenter) {
3729 cn.push({cls : 'container', cn : []});
3736 cls: 'navbar-header',
3741 cls: 'navbar-toggle',
3742 'data-toggle': 'collapse',
3747 html: 'Toggle navigation'
3769 cls: 'collapse navbar-collapse',
3773 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3775 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3776 cfg.cls += ' navbar-' + this.position;
3778 // tag can override this..
3780 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3783 if (this.brand !== '') {
3786 href: this.brand_href ? this.brand_href : '#',
3787 cls: 'navbar-brand',
3795 cfg.cls += ' main-nav';
3803 getHeaderChildContainer : function()
3805 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3806 return this.el.select('.navbar-header',true).first();
3809 return this.getChildContainer();
3813 initEvents : function()
3815 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3817 if (this.autohide) {
3822 Roo.get(document).on('scroll',function(e) {
3823 var ns = Roo.get(document).getScroll().top;
3824 var os = prevScroll;
3828 ft.removeClass('slideDown');
3829 ft.addClass('slideUp');
3832 ft.removeClass('slideUp');
3833 ft.addClass('slideDown');
3854 * @class Roo.bootstrap.NavSidebar
3855 * @extends Roo.bootstrap.Navbar
3856 * Bootstrap Sidebar class
3859 * Create a new Sidebar
3860 * @param {Object} config The config object
3864 Roo.bootstrap.NavSidebar = function(config){
3865 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3868 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3870 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3872 getAutoCreate : function(){
3877 cls: 'sidebar sidebar-nav'
3899 * @class Roo.bootstrap.NavGroup
3900 * @extends Roo.bootstrap.Component
3901 * Bootstrap NavGroup class
3902 * @cfg {String} align (left|right)
3903 * @cfg {Boolean} inverse
3904 * @cfg {String} type (nav|pills|tab) default nav
3905 * @cfg {String} navId - reference Id for navbar.
3909 * Create a new nav group
3910 * @param {Object} config The config object
3913 Roo.bootstrap.NavGroup = function(config){
3914 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3917 Roo.bootstrap.NavGroup.register(this);
3921 * Fires when the active item changes
3922 * @param {Roo.bootstrap.NavGroup} this
3923 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3924 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3931 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3942 getAutoCreate : function()
3944 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3951 if (['tabs','pills'].indexOf(this.type)!==-1) {
3952 cfg.cls += ' nav-' + this.type
3954 if (this.type!=='nav') {
3955 Roo.log('nav type must be nav/tabs/pills')
3957 cfg.cls += ' navbar-nav'
3960 if (this.parent().sidebar) {
3963 cls: 'dashboard-menu sidebar-menu'
3969 if (this.form === true) {
3975 if (this.align === 'right') {
3976 cfg.cls += ' navbar-right';
3978 cfg.cls += ' navbar-left';
3982 if (this.align === 'right') {
3983 cfg.cls += ' navbar-right';
3987 cfg.cls += ' navbar-inverse';
3995 * sets the active Navigation item
3996 * @param {Roo.bootstrap.NavItem} the new current navitem
3998 setActiveItem : function(item)
4001 Roo.each(this.navItems, function(v){
4006 v.setActive(false, true);
4013 item.setActive(true, true);
4014 this.fireEvent('changed', this, item, prev);
4019 * gets the active Navigation item
4020 * @return {Roo.bootstrap.NavItem} the current navitem
4022 getActive : function()
4026 Roo.each(this.navItems, function(v){
4037 indexOfNav : function()
4041 Roo.each(this.navItems, function(v,i){
4052 * adds a Navigation item
4053 * @param {Roo.bootstrap.NavItem} the navitem to add
4055 addItem : function(cfg)
4057 var cn = new Roo.bootstrap.NavItem(cfg);
4059 cn.parentId = this.id;
4060 cn.onRender(this.el, null);
4064 * register a Navigation item
4065 * @param {Roo.bootstrap.NavItem} the navitem to add
4067 register : function(item)
4069 this.navItems.push( item);
4070 item.navId = this.navId;
4075 * clear all the Navigation item
4078 clearAll : function()
4081 this.el.dom.innerHTML = '';
4084 getNavItem: function(tabId)
4087 Roo.each(this.navItems, function(e) {
4088 if (e.tabId == tabId) {
4098 setActiveNext : function()
4100 var i = this.indexOfNav(this.getActive());
4101 if (i > this.navItems.length) {
4104 this.setActiveItem(this.navItems[i+1]);
4106 setActivePrev : function()
4108 var i = this.indexOfNav(this.getActive());
4112 this.setActiveItem(this.navItems[i-1]);
4114 clearWasActive : function(except) {
4115 Roo.each(this.navItems, function(e) {
4116 if (e.tabId != except.tabId && e.was_active) {
4117 e.was_active = false;
4124 getWasActive : function ()
4127 Roo.each(this.navItems, function(e) {
4142 Roo.apply(Roo.bootstrap.NavGroup, {
4146 * register a Navigation Group
4147 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4149 register : function(navgrp)
4151 this.groups[navgrp.navId] = navgrp;
4155 * fetch a Navigation Group based on the navigation ID
4156 * @param {string} the navgroup to add
4157 * @returns {Roo.bootstrap.NavGroup} the navgroup
4159 get: function(navId) {
4160 if (typeof(this.groups[navId]) == 'undefined') {
4162 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4164 return this.groups[navId] ;
4179 * @class Roo.bootstrap.NavItem
4180 * @extends Roo.bootstrap.Component
4181 * Bootstrap Navbar.NavItem class
4182 * @cfg {String} href link to
4183 * @cfg {String} html content of button
4184 * @cfg {String} badge text inside badge
4185 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4186 * @cfg {String} glyphicon name of glyphicon
4187 * @cfg {String} icon name of font awesome icon
4188 * @cfg {Boolean} active Is item active
4189 * @cfg {Boolean} disabled Is item disabled
4191 * @cfg {Boolean} preventDefault (true | false) default false
4192 * @cfg {String} tabId the tab that this item activates.
4193 * @cfg {String} tagtype (a|span) render as a href or span?
4194 * @cfg {Boolean} animateRef (true|false) link to element default false
4197 * Create a new Navbar Item
4198 * @param {Object} config The config object
4200 Roo.bootstrap.NavItem = function(config){
4201 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4206 * The raw click event for the entire grid.
4207 * @param {Roo.EventObject} e
4212 * Fires when the active item active state changes
4213 * @param {Roo.bootstrap.NavItem} this
4214 * @param {boolean} state the new state
4220 * Fires when scroll to element
4221 * @param {Roo.bootstrap.NavItem} this
4222 * @param {Object} options
4223 * @param {Roo.EventObject} e
4231 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4239 preventDefault : false,
4246 getAutoCreate : function(){
4255 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4257 if (this.disabled) {
4258 cfg.cls += ' disabled';
4261 if (this.href || this.html || this.glyphicon || this.icon) {
4265 href : this.href || "#",
4266 html: this.html || ''
4271 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4274 if(this.glyphicon) {
4275 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4280 cfg.cn[0].html += " <span class='caret'></span>";
4284 if (this.badge !== '') {
4286 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4294 initEvents: function()
4296 if (typeof (this.menu) != 'undefined') {
4297 this.menu.parentType = this.xtype;
4298 this.menu.triggerEl = this.el;
4299 this.menu = this.addxtype(Roo.apply({}, this.menu));
4302 this.el.select('a',true).on('click', this.onClick, this);
4304 if(this.tagtype == 'span'){
4305 this.el.select('span',true).on('click', this.onClick, this);
4308 // at this point parent should be available..
4309 this.parent().register(this);
4312 onClick : function(e)
4314 if (e.getTarget('.dropdown-menu-item')) {
4315 // did you click on a menu itemm.... - then don't trigger onclick..
4320 this.preventDefault ||
4323 Roo.log("NavItem - prevent Default?");
4327 if (this.disabled) {
4331 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4332 if (tg && tg.transition) {
4333 Roo.log("waiting for the transitionend");
4339 //Roo.log("fire event clicked");
4340 if(this.fireEvent('click', this, e) === false){
4344 if(this.tagtype == 'span'){
4348 //Roo.log(this.href);
4349 var ael = this.el.select('a',true).first();
4352 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4353 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4354 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4355 return; // ignore... - it's a 'hash' to another page.
4357 Roo.log("NavItem - prevent Default?");
4359 this.scrollToElement(e);
4363 var p = this.parent();
4365 if (['tabs','pills'].indexOf(p.type)!==-1) {
4366 if (typeof(p.setActiveItem) !== 'undefined') {
4367 p.setActiveItem(this);
4371 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4372 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4373 // remove the collapsed menu expand...
4374 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4378 isActive: function () {
4381 setActive : function(state, fire, is_was_active)
4383 if (this.active && !state && this.navId) {
4384 this.was_active = true;
4385 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4387 nv.clearWasActive(this);
4391 this.active = state;
4394 this.el.removeClass('active');
4395 } else if (!this.el.hasClass('active')) {
4396 this.el.addClass('active');
4399 this.fireEvent('changed', this, state);
4402 // show a panel if it's registered and related..
4404 if (!this.navId || !this.tabId || !state || is_was_active) {
4408 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4412 var pan = tg.getPanelByName(this.tabId);
4416 // if we can not flip to new panel - go back to old nav highlight..
4417 if (false == tg.showPanel(pan)) {
4418 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420 var onav = nv.getWasActive();
4422 onav.setActive(true, false, true);
4431 // this should not be here...
4432 setDisabled : function(state)
4434 this.disabled = state;
4436 this.el.removeClass('disabled');
4437 } else if (!this.el.hasClass('disabled')) {
4438 this.el.addClass('disabled');
4444 * Fetch the element to display the tooltip on.
4445 * @return {Roo.Element} defaults to this.el
4447 tooltipEl : function()
4449 return this.el.select('' + this.tagtype + '', true).first();
4452 scrollToElement : function(e)
4454 var c = document.body;
4457 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4459 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4460 c = document.documentElement;
4463 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4469 var o = target.calcOffsetsTo(c);
4476 this.fireEvent('scrollto', this, options, e);
4478 Roo.get(c).scrollTo('top', options.value, true);
4491 * <span> icon </span>
4492 * <span> text </span>
4493 * <span>badge </span>
4497 * @class Roo.bootstrap.NavSidebarItem
4498 * @extends Roo.bootstrap.NavItem
4499 * Bootstrap Navbar.NavSidebarItem class
4500 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4501 * {bool} open is the menu open
4503 * Create a new Navbar Button
4504 * @param {Object} config The config object
4506 Roo.bootstrap.NavSidebarItem = function(config){
4507 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4512 * The raw click event for the entire grid.
4513 * @param {Roo.EventObject} e
4518 * Fires when the active item active state changes
4519 * @param {Roo.bootstrap.NavSidebarItem} this
4520 * @param {boolean} state the new state
4528 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4530 badgeWeight : 'default',
4534 getAutoCreate : function(){
4539 href : this.href || '#',
4551 html : this.html || ''
4556 cfg.cls += ' active';
4559 if (this.disabled) {
4560 cfg.cls += ' disabled';
4563 cfg.cls += ' open x-open';
4566 if (this.glyphicon || this.icon) {
4567 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4568 a.cn.push({ tag : 'i', cls : c }) ;
4573 if (this.badge !== '') {
4575 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4579 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4580 a.cls += 'dropdown-toggle treeview' ;
4588 initEvents : function()
4590 if (typeof (this.menu) != 'undefined') {
4591 this.menu.parentType = this.xtype;
4592 this.menu.triggerEl = this.el;
4593 this.menu = this.addxtype(Roo.apply({}, this.menu));
4596 this.el.on('click', this.onClick, this);
4599 if(this.badge !== ''){
4601 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4606 onClick : function(e)
4613 if(this.preventDefault){
4617 this.fireEvent('click', this);
4620 disable : function()
4622 this.setDisabled(true);
4627 this.setDisabled(false);
4630 setDisabled : function(state)
4632 if(this.disabled == state){
4636 this.disabled = state;
4639 this.el.addClass('disabled');
4643 this.el.removeClass('disabled');
4648 setActive : function(state)
4650 if(this.active == state){
4654 this.active = state;
4657 this.el.addClass('active');
4661 this.el.removeClass('active');
4666 isActive: function ()
4671 setBadge : function(str)
4677 this.badgeEl.dom.innerHTML = str;
4694 * @class Roo.bootstrap.Row
4695 * @extends Roo.bootstrap.Component
4696 * Bootstrap Row class (contains columns...)
4700 * @param {Object} config The config object
4703 Roo.bootstrap.Row = function(config){
4704 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4707 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4709 getAutoCreate : function(){
4728 * @class Roo.bootstrap.Element
4729 * @extends Roo.bootstrap.Component
4730 * Bootstrap Element class
4731 * @cfg {String} html contents of the element
4732 * @cfg {String} tag tag of the element
4733 * @cfg {String} cls class of the element
4734 * @cfg {Boolean} preventDefault (true|false) default false
4735 * @cfg {Boolean} clickable (true|false) default false
4738 * Create a new Element
4739 * @param {Object} config The config object
4742 Roo.bootstrap.Element = function(config){
4743 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4749 * When a element is chick
4750 * @param {Roo.bootstrap.Element} this
4751 * @param {Roo.EventObject} e
4757 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4762 preventDefault: false,
4765 getAutoCreate : function(){
4776 initEvents: function()
4778 Roo.bootstrap.Element.superclass.initEvents.call(this);
4781 this.el.on('click', this.onClick, this);
4786 onClick : function(e)
4788 if(this.preventDefault){
4792 this.fireEvent('click', this, e);
4795 getValue : function()
4797 return this.el.dom.innerHTML;
4800 setValue : function(value)
4802 this.el.dom.innerHTML = value;
4817 * @class Roo.bootstrap.Pagination
4818 * @extends Roo.bootstrap.Component
4819 * Bootstrap Pagination class
4820 * @cfg {String} size xs | sm | md | lg
4821 * @cfg {Boolean} inverse false | true
4824 * Create a new Pagination
4825 * @param {Object} config The config object
4828 Roo.bootstrap.Pagination = function(config){
4829 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4832 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4838 getAutoCreate : function(){
4844 cfg.cls += ' inverse';
4850 cfg.cls += " " + this.cls;
4868 * @class Roo.bootstrap.PaginationItem
4869 * @extends Roo.bootstrap.Component
4870 * Bootstrap PaginationItem class
4871 * @cfg {String} html text
4872 * @cfg {String} href the link
4873 * @cfg {Boolean} preventDefault (true | false) default true
4874 * @cfg {Boolean} active (true | false) default false
4875 * @cfg {Boolean} disabled default false
4879 * Create a new PaginationItem
4880 * @param {Object} config The config object
4884 Roo.bootstrap.PaginationItem = function(config){
4885 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4890 * The raw click event for the entire grid.
4891 * @param {Roo.EventObject} e
4897 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4901 preventDefault: true,
4906 getAutoCreate : function(){
4912 href : this.href ? this.href : '#',
4913 html : this.html ? this.html : ''
4923 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4927 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4933 initEvents: function() {
4935 this.el.on('click', this.onClick, this);
4938 onClick : function(e)
4940 Roo.log('PaginationItem on click ');
4941 if(this.preventDefault){
4949 this.fireEvent('click', this, e);
4965 * @class Roo.bootstrap.Slider
4966 * @extends Roo.bootstrap.Component
4967 * Bootstrap Slider class
4970 * Create a new Slider
4971 * @param {Object} config The config object
4974 Roo.bootstrap.Slider = function(config){
4975 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4978 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4980 getAutoCreate : function(){
4984 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4988 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5000 * Ext JS Library 1.1.1
5001 * Copyright(c) 2006-2007, Ext JS, LLC.
5003 * Originally Released Under LGPL - original licence link has changed is not relivant.
5006 * <script type="text/javascript">
5011 * @class Roo.grid.ColumnModel
5012 * @extends Roo.util.Observable
5013 * This is the default implementation of a ColumnModel used by the Grid. It defines
5014 * the columns in the grid.
5017 var colModel = new Roo.grid.ColumnModel([
5018 {header: "Ticker", width: 60, sortable: true, locked: true},
5019 {header: "Company Name", width: 150, sortable: true},
5020 {header: "Market Cap.", width: 100, sortable: true},
5021 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5022 {header: "Employees", width: 100, sortable: true, resizable: false}
5027 * The config options listed for this class are options which may appear in each
5028 * individual column definition.
5029 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5031 * @param {Object} config An Array of column config objects. See this class's
5032 * config objects for details.
5034 Roo.grid.ColumnModel = function(config){
5036 * The config passed into the constructor
5038 this.config = config;
5041 // if no id, create one
5042 // if the column does not have a dataIndex mapping,
5043 // map it to the order it is in the config
5044 for(var i = 0, len = config.length; i < len; i++){
5046 if(typeof c.dataIndex == "undefined"){
5049 if(typeof c.renderer == "string"){
5050 c.renderer = Roo.util.Format[c.renderer];
5052 if(typeof c.id == "undefined"){
5055 if(c.editor && c.editor.xtype){
5056 c.editor = Roo.factory(c.editor, Roo.grid);
5058 if(c.editor && c.editor.isFormField){
5059 c.editor = new Roo.grid.GridEditor(c.editor);
5061 this.lookup[c.id] = c;
5065 * The width of columns which have no width specified (defaults to 100)
5068 this.defaultWidth = 100;
5071 * Default sortable of columns which have no sortable specified (defaults to false)
5074 this.defaultSortable = false;
5078 * @event widthchange
5079 * Fires when the width of a column changes.
5080 * @param {ColumnModel} this
5081 * @param {Number} columnIndex The column index
5082 * @param {Number} newWidth The new width
5084 "widthchange": true,
5086 * @event headerchange
5087 * Fires when the text of a header changes.
5088 * @param {ColumnModel} this
5089 * @param {Number} columnIndex The column index
5090 * @param {Number} newText The new header text
5092 "headerchange": true,
5094 * @event hiddenchange
5095 * Fires when a column is hidden or "unhidden".
5096 * @param {ColumnModel} this
5097 * @param {Number} columnIndex The column index
5098 * @param {Boolean} hidden true if hidden, false otherwise
5100 "hiddenchange": true,
5102 * @event columnmoved
5103 * Fires when a column is moved.
5104 * @param {ColumnModel} this
5105 * @param {Number} oldIndex
5106 * @param {Number} newIndex
5108 "columnmoved" : true,
5110 * @event columlockchange
5111 * Fires when a column's locked state is changed
5112 * @param {ColumnModel} this
5113 * @param {Number} colIndex
5114 * @param {Boolean} locked true if locked
5116 "columnlockchange" : true
5118 Roo.grid.ColumnModel.superclass.constructor.call(this);
5120 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5122 * @cfg {String} header The header text to display in the Grid view.
5125 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5126 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5127 * specified, the column's index is used as an index into the Record's data Array.
5130 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5131 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5134 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5135 * Defaults to the value of the {@link #defaultSortable} property.
5136 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5139 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5142 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5145 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5148 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5151 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5152 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5153 * default renderer uses the raw data value. If an object is returned (bootstrap only)
5154 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5157 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5160 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5163 * @cfg {String} cursor (Optional)
5166 * @cfg {String} tooltip (Optional)
5169 * @cfg {Number} xs (Optional)
5172 * @cfg {Number} sm (Optional)
5175 * @cfg {Number} md (Optional)
5178 * @cfg {Number} lg (Optional)
5181 * Returns the id of the column at the specified index.
5182 * @param {Number} index The column index
5183 * @return {String} the id
5185 getColumnId : function(index){
5186 return this.config[index].id;
5190 * Returns the column for a specified id.
5191 * @param {String} id The column id
5192 * @return {Object} the column
5194 getColumnById : function(id){
5195 return this.lookup[id];
5200 * Returns the column for a specified dataIndex.
5201 * @param {String} dataIndex The column dataIndex
5202 * @return {Object|Boolean} the column or false if not found
5204 getColumnByDataIndex: function(dataIndex){
5205 var index = this.findColumnIndex(dataIndex);
5206 return index > -1 ? this.config[index] : false;
5210 * Returns the index for a specified column id.
5211 * @param {String} id The column id
5212 * @return {Number} the index, or -1 if not found
5214 getIndexById : function(id){
5215 for(var i = 0, len = this.config.length; i < len; i++){
5216 if(this.config[i].id == id){
5224 * Returns the index for a specified column dataIndex.
5225 * @param {String} dataIndex The column dataIndex
5226 * @return {Number} the index, or -1 if not found
5229 findColumnIndex : function(dataIndex){
5230 for(var i = 0, len = this.config.length; i < len; i++){
5231 if(this.config[i].dataIndex == dataIndex){
5239 moveColumn : function(oldIndex, newIndex){
5240 var c = this.config[oldIndex];
5241 this.config.splice(oldIndex, 1);
5242 this.config.splice(newIndex, 0, c);
5243 this.dataMap = null;
5244 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5247 isLocked : function(colIndex){
5248 return this.config[colIndex].locked === true;
5251 setLocked : function(colIndex, value, suppressEvent){
5252 if(this.isLocked(colIndex) == value){
5255 this.config[colIndex].locked = value;
5257 this.fireEvent("columnlockchange", this, colIndex, value);
5261 getTotalLockedWidth : function(){
5263 for(var i = 0; i < this.config.length; i++){
5264 if(this.isLocked(i) && !this.isHidden(i)){
5265 this.totalWidth += this.getColumnWidth(i);
5271 getLockedCount : function(){
5272 for(var i = 0, len = this.config.length; i < len; i++){
5273 if(!this.isLocked(i)){
5278 return this.config.length;
5282 * Returns the number of columns.
5285 getColumnCount : function(visibleOnly){
5286 if(visibleOnly === true){
5288 for(var i = 0, len = this.config.length; i < len; i++){
5289 if(!this.isHidden(i)){
5295 return this.config.length;
5299 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5300 * @param {Function} fn
5301 * @param {Object} scope (optional)
5302 * @return {Array} result
5304 getColumnsBy : function(fn, scope){
5306 for(var i = 0, len = this.config.length; i < len; i++){
5307 var c = this.config[i];
5308 if(fn.call(scope||this, c, i) === true){
5316 * Returns true if the specified column is sortable.
5317 * @param {Number} col The column index
5320 isSortable : function(col){
5321 if(typeof this.config[col].sortable == "undefined"){
5322 return this.defaultSortable;
5324 return this.config[col].sortable;
5328 * Returns the rendering (formatting) function defined for the column.
5329 * @param {Number} col The column index.
5330 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5332 getRenderer : function(col){
5333 if(!this.config[col].renderer){
5334 return Roo.grid.ColumnModel.defaultRenderer;
5336 return this.config[col].renderer;
5340 * Sets the rendering (formatting) function for a column.
5341 * @param {Number} col The column index
5342 * @param {Function} fn The function to use to process the cell's raw data
5343 * to return HTML markup for the grid view. The render function is called with
5344 * the following parameters:<ul>
5345 * <li>Data value.</li>
5346 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5347 * <li>css A CSS style string to apply to the table cell.</li>
5348 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5349 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5350 * <li>Row index</li>
5351 * <li>Column index</li>
5352 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5354 setRenderer : function(col, fn){
5355 this.config[col].renderer = fn;
5359 * Returns the width for the specified column.
5360 * @param {Number} col The column index
5363 getColumnWidth : function(col){
5364 return this.config[col].width * 1 || this.defaultWidth;
5368 * Sets the width for a column.
5369 * @param {Number} col The column index
5370 * @param {Number} width The new width
5372 setColumnWidth : function(col, width, suppressEvent){
5373 this.config[col].width = width;
5374 this.totalWidth = null;
5376 this.fireEvent("widthchange", this, col, width);
5381 * Returns the total width of all columns.
5382 * @param {Boolean} includeHidden True to include hidden column widths
5385 getTotalWidth : function(includeHidden){
5386 if(!this.totalWidth){
5387 this.totalWidth = 0;
5388 for(var i = 0, len = this.config.length; i < len; i++){
5389 if(includeHidden || !this.isHidden(i)){
5390 this.totalWidth += this.getColumnWidth(i);
5394 return this.totalWidth;
5398 * Returns the header for the specified column.
5399 * @param {Number} col The column index
5402 getColumnHeader : function(col){
5403 return this.config[col].header;
5407 * Sets the header for a column.
5408 * @param {Number} col The column index
5409 * @param {String} header The new header
5411 setColumnHeader : function(col, header){
5412 this.config[col].header = header;
5413 this.fireEvent("headerchange", this, col, header);
5417 * Returns the tooltip for the specified column.
5418 * @param {Number} col The column index
5421 getColumnTooltip : function(col){
5422 return this.config[col].tooltip;
5425 * Sets the tooltip for a column.
5426 * @param {Number} col The column index
5427 * @param {String} tooltip The new tooltip
5429 setColumnTooltip : function(col, tooltip){
5430 this.config[col].tooltip = tooltip;
5434 * Returns the dataIndex for the specified column.
5435 * @param {Number} col The column index
5438 getDataIndex : function(col){
5439 return this.config[col].dataIndex;
5443 * Sets the dataIndex for a column.
5444 * @param {Number} col The column index
5445 * @param {Number} dataIndex The new dataIndex
5447 setDataIndex : function(col, dataIndex){
5448 this.config[col].dataIndex = dataIndex;
5454 * Returns true if the cell is editable.
5455 * @param {Number} colIndex The column index
5456 * @param {Number} rowIndex The row index - this is nto actually used..?
5459 isCellEditable : function(colIndex, rowIndex){
5460 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5464 * Returns the editor defined for the cell/column.
5465 * return false or null to disable editing.
5466 * @param {Number} colIndex The column index
5467 * @param {Number} rowIndex The row index
5470 getCellEditor : function(colIndex, rowIndex){
5471 return this.config[colIndex].editor;
5475 * Sets if a column is editable.
5476 * @param {Number} col The column index
5477 * @param {Boolean} editable True if the column is editable
5479 setEditable : function(col, editable){
5480 this.config[col].editable = editable;
5485 * Returns true if the column is hidden.
5486 * @param {Number} colIndex The column index
5489 isHidden : function(colIndex){
5490 return this.config[colIndex].hidden;
5495 * Returns true if the column width cannot be changed
5497 isFixed : function(colIndex){
5498 return this.config[colIndex].fixed;
5502 * Returns true if the column can be resized
5505 isResizable : function(colIndex){
5506 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5509 * Sets if a column is hidden.
5510 * @param {Number} colIndex The column index
5511 * @param {Boolean} hidden True if the column is hidden
5513 setHidden : function(colIndex, hidden){
5514 this.config[colIndex].hidden = hidden;
5515 this.totalWidth = null;
5516 this.fireEvent("hiddenchange", this, colIndex, hidden);
5520 * Sets the editor for a column.
5521 * @param {Number} col The column index
5522 * @param {Object} editor The editor object
5524 setEditor : function(col, editor){
5525 this.config[col].editor = editor;
5529 Roo.grid.ColumnModel.defaultRenderer = function(value){
5530 if(typeof value == "string" && value.length < 1){
5536 // Alias for backwards compatibility
5537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5540 * Ext JS Library 1.1.1
5541 * Copyright(c) 2006-2007, Ext JS, LLC.
5543 * Originally Released Under LGPL - original licence link has changed is not relivant.
5546 * <script type="text/javascript">
5550 * @class Roo.LoadMask
5551 * A simple utility class for generically masking elements while loading data. If the element being masked has
5552 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5553 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5554 * element's UpdateManager load indicator and will be destroyed after the initial load.
5556 * Create a new LoadMask
5557 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5558 * @param {Object} config The config object
5560 Roo.LoadMask = function(el, config){
5561 this.el = Roo.get(el);
5562 Roo.apply(this, config);
5564 this.store.on('beforeload', this.onBeforeLoad, this);
5565 this.store.on('load', this.onLoad, this);
5566 this.store.on('loadexception', this.onLoadException, this);
5567 this.removeMask = false;
5569 var um = this.el.getUpdateManager();
5570 um.showLoadIndicator = false; // disable the default indicator
5571 um.on('beforeupdate', this.onBeforeLoad, this);
5572 um.on('update', this.onLoad, this);
5573 um.on('failure', this.onLoad, this);
5574 this.removeMask = true;
5578 Roo.LoadMask.prototype = {
5580 * @cfg {Boolean} removeMask
5581 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5582 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5586 * The text to display in a centered loading message box (defaults to 'Loading...')
5590 * @cfg {String} msgCls
5591 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5593 msgCls : 'x-mask-loading',
5596 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5602 * Disables the mask to prevent it from being displayed
5604 disable : function(){
5605 this.disabled = true;
5609 * Enables the mask so that it can be displayed
5611 enable : function(){
5612 this.disabled = false;
5615 onLoadException : function()
5619 if (typeof(arguments[3]) != 'undefined') {
5620 Roo.MessageBox.alert("Error loading",arguments[3]);
5624 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5625 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5634 this.el.unmask(this.removeMask);
5639 this.el.unmask(this.removeMask);
5643 onBeforeLoad : function(){
5645 this.el.mask(this.msg, this.msgCls);
5650 destroy : function(){
5652 this.store.un('beforeload', this.onBeforeLoad, this);
5653 this.store.un('load', this.onLoad, this);
5654 this.store.un('loadexception', this.onLoadException, this);
5656 var um = this.el.getUpdateManager();
5657 um.un('beforeupdate', this.onBeforeLoad, this);
5658 um.un('update', this.onLoad, this);
5659 um.un('failure', this.onLoad, this);
5670 * @class Roo.bootstrap.Table
5671 * @extends Roo.bootstrap.Component
5672 * Bootstrap Table class
5673 * @cfg {String} cls table class
5674 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5675 * @cfg {String} bgcolor Specifies the background color for a table
5676 * @cfg {Number} border Specifies whether the table cells should have borders or not
5677 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5678 * @cfg {Number} cellspacing Specifies the space between cells
5679 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5680 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5681 * @cfg {String} sortable Specifies that the table should be sortable
5682 * @cfg {String} summary Specifies a summary of the content of a table
5683 * @cfg {Number} width Specifies the width of a table
5684 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5686 * @cfg {boolean} striped Should the rows be alternative striped
5687 * @cfg {boolean} bordered Add borders to the table
5688 * @cfg {boolean} hover Add hover highlighting
5689 * @cfg {boolean} condensed Format condensed
5690 * @cfg {boolean} responsive Format condensed
5691 * @cfg {Boolean} loadMask (true|false) default false
5692 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5693 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5694 * @cfg {Boolean} rowSelection (true|false) default false
5695 * @cfg {Boolean} cellSelection (true|false) default false
5696 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5697 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5701 * Create a new Table
5702 * @param {Object} config The config object
5705 Roo.bootstrap.Table = function(config){
5706 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5711 this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5712 this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5713 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5714 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5718 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5719 this.sm = this.selModel;
5720 this.sm.xmodule = this.xmodule || false;
5722 if (this.cm && typeof(this.cm.config) == 'undefined') {
5723 this.colModel = new Roo.grid.ColumnModel(this.cm);
5724 this.cm = this.colModel;
5725 this.cm.xmodule = this.xmodule || false;
5728 this.store= Roo.factory(this.store, Roo.data);
5729 this.ds = this.store;
5730 this.ds.xmodule = this.xmodule || false;
5733 if (this.footer && this.store) {
5734 this.footer.dataSource = this.ds;
5735 this.footer = Roo.factory(this.footer);
5742 * Fires when a cell is clicked
5743 * @param {Roo.bootstrap.Table} this
5744 * @param {Roo.Element} el
5745 * @param {Number} rowIndex
5746 * @param {Number} columnIndex
5747 * @param {Roo.EventObject} e
5751 * @event celldblclick
5752 * Fires when a cell is double clicked
5753 * @param {Roo.bootstrap.Table} this
5754 * @param {Roo.Element} el
5755 * @param {Number} rowIndex
5756 * @param {Number} columnIndex
5757 * @param {Roo.EventObject} e
5759 "celldblclick" : true,
5762 * Fires when a row is clicked
5763 * @param {Roo.bootstrap.Table} this
5764 * @param {Roo.Element} el
5765 * @param {Number} rowIndex
5766 * @param {Roo.EventObject} e
5770 * @event rowdblclick
5771 * Fires when a row is double clicked
5772 * @param {Roo.bootstrap.Table} this
5773 * @param {Roo.Element} el
5774 * @param {Number} rowIndex
5775 * @param {Roo.EventObject} e
5777 "rowdblclick" : true,
5780 * Fires when a mouseover occur
5781 * @param {Roo.bootstrap.Table} this
5782 * @param {Roo.Element} el
5783 * @param {Number} rowIndex
5784 * @param {Number} columnIndex
5785 * @param {Roo.EventObject} e
5790 * Fires when a mouseout occur
5791 * @param {Roo.bootstrap.Table} this
5792 * @param {Roo.Element} el
5793 * @param {Number} rowIndex
5794 * @param {Number} columnIndex
5795 * @param {Roo.EventObject} e
5800 * Fires when a row is rendered, so you can change add a style to it.
5801 * @param {Roo.bootstrap.Table} this
5802 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5806 * @event rowsrendered
5807 * Fires when all the rows have been rendered
5808 * @param {Roo.bootstrap.Table} this
5810 'rowsrendered' : true,
5812 * @event contextmenu
5813 * The raw contextmenu event for the entire grid.
5814 * @param {Roo.EventObject} e
5816 "contextmenu" : true,
5818 * @event rowcontextmenu
5819 * Fires when a row is right clicked
5820 * @param {Roo.bootstrap.Table} this
5821 * @param {Number} rowIndex
5822 * @param {Roo.EventObject} e
5824 "rowcontextmenu" : true,
5826 * @event cellcontextmenu
5827 * Fires when a cell is right clicked
5828 * @param {Roo.bootstrap.Table} this
5829 * @param {Number} rowIndex
5830 * @param {Number} cellIndex
5831 * @param {Roo.EventObject} e
5833 "cellcontextmenu" : true,
5835 * @event headercontextmenu
5836 * Fires when a header is right clicked
5837 * @param {Roo.bootstrap.Table} this
5838 * @param {Number} columnIndex
5839 * @param {Roo.EventObject} e
5841 "headercontextmenu" : true
5845 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5871 rowSelection : false,
5872 cellSelection : false,
5875 // Roo.Element - the tbody
5877 // Roo.Element - thead element
5880 container: false, // used by gridpanel...
5882 getAutoCreate : function()
5884 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5891 if (this.scrollBody) {
5892 cfg.cls += ' table-body-fixed';
5895 cfg.cls += ' table-striped';
5899 cfg.cls += ' table-hover';
5901 if (this.bordered) {
5902 cfg.cls += ' table-bordered';
5904 if (this.condensed) {
5905 cfg.cls += ' table-condensed';
5907 if (this.responsive) {
5908 cfg.cls += ' table-responsive';
5912 cfg.cls+= ' ' +this.cls;
5915 // this lot should be simplifed...
5918 cfg.align=this.align;
5921 cfg.bgcolor=this.bgcolor;
5924 cfg.border=this.border;
5926 if (this.cellpadding) {
5927 cfg.cellpadding=this.cellpadding;
5929 if (this.cellspacing) {
5930 cfg.cellspacing=this.cellspacing;
5933 cfg.frame=this.frame;
5936 cfg.rules=this.rules;
5938 if (this.sortable) {
5939 cfg.sortable=this.sortable;
5942 cfg.summary=this.summary;
5945 cfg.width=this.width;
5948 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5951 if(this.store || this.cm){
5952 if(this.headerShow){
5953 cfg.cn.push(this.renderHeader());
5956 cfg.cn.push(this.renderBody());
5958 if(this.footerShow){
5959 cfg.cn.push(this.renderFooter());
5961 // where does this come from?
5962 //cfg.cls+= ' TableGrid';
5965 return { cn : [ cfg ] };
5968 initEvents : function()
5970 if(!this.store || !this.cm){
5974 //Roo.log('initEvents with ds!!!!');
5976 this.mainBody = this.el.select('tbody', true).first();
5977 this.mainHead = this.el.select('thead', true).first();
5983 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5984 e.on('click', _this.sort, _this);
5987 this.el.on("click", this.onClick, this);
5988 this.el.on("dblclick", this.onDblClick, this);
5990 // why is this done????? = it breaks dialogs??
5991 //this.parent().el.setStyle('position', 'relative');
5995 this.footer.parentId = this.id;
5996 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5999 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6001 this.store.on('load', this.onLoad, this);
6002 this.store.on('beforeload', this.onBeforeLoad, this);
6003 this.store.on('update', this.onUpdate, this);
6004 this.store.on('add', this.onAdd, this);
6006 this.el.on("contextmenu", this.onContextMenu, this);
6008 this.mainBody.on('scroll', this.onBodyScroll, this);
6013 onContextMenu : function(e, t)
6015 this.processEvent("contextmenu", e);
6018 processEvent : function(name, e)
6020 if (name != 'touchstart' ) {
6021 this.fireEvent(name, e);
6024 var t = e.getTarget();
6026 var cell = Roo.get(t);
6032 if(cell.findParent('tfoot', false, true)){
6036 if(cell.findParent('thead', false, true)){
6038 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6039 cell = Roo.get(t).findParent('th', false, true);
6041 Roo.log("failed to find th in thead?");
6042 Roo.log(e.getTarget());
6047 var cellIndex = cell.dom.cellIndex;
6049 var ename = name == 'touchstart' ? 'click' : name;
6050 this.fireEvent("header" + ename, this, cellIndex, e);
6055 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6056 cell = Roo.get(t).findParent('td', false, true);
6058 Roo.log("failed to find th in tbody?");
6059 Roo.log(e.getTarget());
6064 var row = cell.findParent('tr', false, true);
6065 var cellIndex = cell.dom.cellIndex;
6066 var rowIndex = row.dom.rowIndex - 1;
6070 this.fireEvent("row" + name, this, rowIndex, e);
6074 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6080 onMouseover : function(e, el)
6082 var cell = Roo.get(el);
6088 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6089 cell = cell.findParent('td', false, true);
6092 var row = cell.findParent('tr', false, true);
6093 var cellIndex = cell.dom.cellIndex;
6094 var rowIndex = row.dom.rowIndex - 1; // start from 0
6096 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6100 onMouseout : function(e, el)
6102 var cell = Roo.get(el);
6108 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6109 cell = cell.findParent('td', false, true);
6112 var row = cell.findParent('tr', false, true);
6113 var cellIndex = cell.dom.cellIndex;
6114 var rowIndex = row.dom.rowIndex - 1; // start from 0
6116 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6120 onClick : function(e, el)
6122 var cell = Roo.get(el);
6124 if(!cell || (!this.cellSelection && !this.rowSelection)){
6128 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6129 cell = cell.findParent('td', false, true);
6132 if(!cell || typeof(cell) == 'undefined'){
6136 var row = cell.findParent('tr', false, true);
6138 if(!row || typeof(row) == 'undefined'){
6142 var cellIndex = cell.dom.cellIndex;
6143 var rowIndex = this.getRowIndex(row);
6145 // why??? - should these not be based on SelectionModel?
6146 if(this.cellSelection){
6147 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6150 if(this.rowSelection){
6151 this.fireEvent('rowclick', this, row, rowIndex, e);
6157 onDblClick : function(e,el)
6159 var cell = Roo.get(el);
6161 if(!cell || (!this.CellSelection && !this.RowSelection)){
6165 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166 cell = cell.findParent('td', false, true);
6169 if(!cell || typeof(cell) == 'undefined'){
6173 var row = cell.findParent('tr', false, true);
6175 if(!row || typeof(row) == 'undefined'){
6179 var cellIndex = cell.dom.cellIndex;
6180 var rowIndex = this.getRowIndex(row);
6182 if(this.CellSelection){
6183 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6186 if(this.RowSelection){
6187 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6191 sort : function(e,el)
6193 var col = Roo.get(el);
6195 if(!col.hasClass('sortable')){
6199 var sort = col.attr('sort');
6202 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6206 this.store.sortInfo = {field : sort, direction : dir};
6209 Roo.log("calling footer first");
6210 this.footer.onClick('first');
6213 this.store.load({ params : { start : 0 } });
6217 renderHeader : function()
6225 this.totalWidth = 0;
6227 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6229 var config = cm.config[i];
6234 html: cm.getColumnHeader(i)
6239 if(typeof(config.sortable) != 'undefined' && config.sortable){
6241 c.html = '<i class="glyphicon"></i>' + c.html;
6244 if(typeof(config.lgHeader) != 'undefined'){
6245 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6248 if(typeof(config.mdHeader) != 'undefined'){
6249 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6252 if(typeof(config.smHeader) != 'undefined'){
6253 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6256 if(typeof(config.xsHeader) != 'undefined'){
6257 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6264 if(typeof(config.tooltip) != 'undefined'){
6265 c.tooltip = config.tooltip;
6268 if(typeof(config.colspan) != 'undefined'){
6269 c.colspan = config.colspan;
6272 if(typeof(config.hidden) != 'undefined' && config.hidden){
6273 c.style += ' display:none;';
6276 if(typeof(config.dataIndex) != 'undefined'){
6277 c.sort = config.dataIndex;
6282 if(typeof(config.align) != 'undefined' && config.align.length){
6283 c.style += ' text-align:' + config.align + ';';
6286 if(typeof(config.width) != 'undefined'){
6287 c.style += ' width:' + config.width + 'px;';
6288 this.totalWidth += config.width;
6290 this.totalWidth += 100; // assume minimum of 100 per column?
6293 if(typeof(config.cls) != 'undefined'){
6294 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6297 ['xs','sm','md','lg'].map(function(size){
6299 if(typeof(config[size]) == 'undefined'){
6303 if (!config[size]) { // 0 = hidden
6304 c.cls += ' hidden-' + size;
6308 c.cls += ' col-' + size + '-' + config[size];
6318 renderBody : function()
6328 colspan : this.cm.getColumnCount()
6338 renderFooter : function()
6348 colspan : this.cm.getColumnCount()
6362 // Roo.log('ds onload');
6367 var ds = this.store;
6369 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6370 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6371 if (_this.store.sortInfo) {
6373 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6374 e.select('i', true).addClass(['glyphicon-arrow-up']);
6377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6378 e.select('i', true).addClass(['glyphicon-arrow-down']);
6383 var tbody = this.mainBody;
6385 if(ds.getCount() > 0){
6386 ds.data.each(function(d,rowIndex){
6387 var row = this.renderRow(cm, ds, rowIndex);
6389 tbody.createChild(row);
6393 if(row.cellObjects.length){
6394 Roo.each(row.cellObjects, function(r){
6395 _this.renderCellObject(r);
6402 Roo.each(this.el.select('tbody td', true).elements, function(e){
6403 e.on('mouseover', _this.onMouseover, _this);
6406 Roo.each(this.el.select('tbody td', true).elements, function(e){
6407 e.on('mouseout', _this.onMouseout, _this);
6409 this.fireEvent('rowsrendered', this);
6410 //if(this.loadMask){
6411 // this.maskEl.hide();
6418 onUpdate : function(ds,record)
6420 this.refreshRow(record);
6423 onRemove : function(ds, record, index, isUpdate){
6424 if(isUpdate !== true){
6425 this.fireEvent("beforerowremoved", this, index, record);
6427 var bt = this.mainBody.dom;
6429 var rows = this.el.select('tbody > tr', true).elements;
6431 if(typeof(rows[index]) != 'undefined'){
6432 bt.removeChild(rows[index].dom);
6435 // if(bt.rows[index]){
6436 // bt.removeChild(bt.rows[index]);
6439 if(isUpdate !== true){
6440 //this.stripeRows(index);
6441 //this.syncRowHeights(index, index);
6443 this.fireEvent("rowremoved", this, index, record);
6447 onAdd : function(ds, records, rowIndex)
6449 //Roo.log('on Add called');
6450 // - note this does not handle multiple adding very well..
6451 var bt = this.mainBody.dom;
6452 for (var i =0 ; i < records.length;i++) {
6453 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6454 //Roo.log(records[i]);
6455 //Roo.log(this.store.getAt(rowIndex+i));
6456 this.insertRow(this.store, rowIndex + i, false);
6463 refreshRow : function(record){
6464 var ds = this.store, index;
6465 if(typeof record == 'number'){
6467 record = ds.getAt(index);
6469 index = ds.indexOf(record);
6471 this.insertRow(ds, index, true);
6472 this.onRemove(ds, record, index+1, true);
6473 //this.syncRowHeights(index, index);
6475 this.fireEvent("rowupdated", this, index, record);
6478 insertRow : function(dm, rowIndex, isUpdate){
6481 this.fireEvent("beforerowsinserted", this, rowIndex);
6483 //var s = this.getScrollState();
6484 var row = this.renderRow(this.cm, this.store, rowIndex);
6485 // insert before rowIndex..
6486 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6490 if(row.cellObjects.length){
6491 Roo.each(row.cellObjects, function(r){
6492 _this.renderCellObject(r);
6497 this.fireEvent("rowsinserted", this, rowIndex);
6498 //this.syncRowHeights(firstRow, lastRow);
6499 //this.stripeRows(firstRow);
6506 getRowDom : function(rowIndex)
6508 var rows = this.el.select('tbody > tr', true).elements;
6510 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6513 // returns the object tree for a tr..
6516 renderRow : function(cm, ds, rowIndex)
6519 var d = ds.getAt(rowIndex);
6526 var cellObjects = [];
6528 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6529 var config = cm.config[i];
6531 var renderer = cm.getRenderer(i);
6535 if(typeof(renderer) !== 'undefined'){
6536 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6538 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6539 // and are rendered into the cells after the row is rendered - using the id for the element.
6541 if(typeof(value) === 'object'){
6551 rowIndex : rowIndex,
6556 this.fireEvent('rowclass', this, rowcfg);
6560 cls : rowcfg.rowClass,
6562 html: (typeof(value) === 'object') ? '' : value
6569 if(typeof(config.colspan) != 'undefined'){
6570 td.colspan = config.colspan;
6573 if(typeof(config.hidden) != 'undefined' && config.hidden){
6574 td.style += ' display:none;';
6577 if(typeof(config.align) != 'undefined' && config.align.length){
6578 td.style += ' text-align:' + config.align + ';';
6581 if(typeof(config.width) != 'undefined'){
6582 td.style += ' width:' + config.width + 'px;';
6585 if(typeof(config.cursor) != 'undefined'){
6586 td.style += ' cursor:' + config.cursor + ';';
6589 if(typeof(config.cls) != 'undefined'){
6590 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6593 ['xs','sm','md','lg'].map(function(size){
6595 if(typeof(config[size]) == 'undefined'){
6599 if (!config[size]) { // 0 = hidden
6600 td.cls += ' hidden-' + size;
6604 td.cls += ' col-' + size + '-' + config[size];
6612 row.cellObjects = cellObjects;
6620 onBeforeLoad : function()
6622 //Roo.log('ds onBeforeLoad');
6626 //if(this.loadMask){
6627 // this.maskEl.show();
6635 this.el.select('tbody', true).first().dom.innerHTML = '';
6638 * Show or hide a row.
6639 * @param {Number} rowIndex to show or hide
6640 * @param {Boolean} state hide
6642 setRowVisibility : function(rowIndex, state)
6644 var bt = this.mainBody.dom;
6646 var rows = this.el.select('tbody > tr', true).elements;
6648 if(typeof(rows[rowIndex]) == 'undefined'){
6651 rows[rowIndex].dom.style.display = state ? '' : 'none';
6655 getSelectionModel : function(){
6657 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6659 return this.selModel;
6662 * Render the Roo.bootstrap object from renderder
6664 renderCellObject : function(r)
6668 var t = r.cfg.render(r.container);
6671 Roo.each(r.cfg.cn, function(c){
6673 container: t.getChildContainer(),
6676 _this.renderCellObject(child);
6681 getRowIndex : function(row)
6685 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6696 * Returns the grid's underlying element = used by panel.Grid
6697 * @return {Element} The element
6699 getGridEl : function(){
6703 * Forces a resize - used by panel.Grid
6704 * @return {Element} The element
6706 autoSize : function(){
6707 //var ctr = Roo.get(this.container.dom.parentElement);
6708 var ctr = Roo.get(this.el.dom);
6710 var thd = this.getGridEl().select('thead',true).first();
6711 var tbd = this.getGridEl().select('tbody', true).first();
6714 var cw = ctr.getWidth();
6718 tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6719 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6722 cw = Math.max(cw, this.totalWidth);
6723 this.getGridEl().select('tr',true).setWidth(cw);
6725 return; // we doe not have a view in this design..
6728 onBodyScroll: function()
6731 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6732 this.mainHead.setStyle({
6733 'position' : 'relative',
6734 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6751 * @class Roo.bootstrap.TableCell
6752 * @extends Roo.bootstrap.Component
6753 * Bootstrap TableCell class
6754 * @cfg {String} html cell contain text
6755 * @cfg {String} cls cell class
6756 * @cfg {String} tag cell tag (td|th) default td
6757 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6758 * @cfg {String} align Aligns the content in a cell
6759 * @cfg {String} axis Categorizes cells
6760 * @cfg {String} bgcolor Specifies the background color of a cell
6761 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6762 * @cfg {Number} colspan Specifies the number of columns a cell should span
6763 * @cfg {String} headers Specifies one or more header cells a cell is related to
6764 * @cfg {Number} height Sets the height of a cell
6765 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6766 * @cfg {Number} rowspan Sets the number of rows a cell should span
6767 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6768 * @cfg {String} valign Vertical aligns the content in a cell
6769 * @cfg {Number} width Specifies the width of a cell
6772 * Create a new TableCell
6773 * @param {Object} config The config object
6776 Roo.bootstrap.TableCell = function(config){
6777 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6780 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
6800 getAutoCreate : function(){
6801 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6821 cfg.align=this.align
6827 cfg.bgcolor=this.bgcolor
6830 cfg.charoff=this.charoff
6833 cfg.colspan=this.colspan
6836 cfg.headers=this.headers
6839 cfg.height=this.height
6842 cfg.nowrap=this.nowrap
6845 cfg.rowspan=this.rowspan
6848 cfg.scope=this.scope
6851 cfg.valign=this.valign
6854 cfg.width=this.width
6873 * @class Roo.bootstrap.TableRow
6874 * @extends Roo.bootstrap.Component
6875 * Bootstrap TableRow class
6876 * @cfg {String} cls row class
6877 * @cfg {String} align Aligns the content in a table row
6878 * @cfg {String} bgcolor Specifies a background color for a table row
6879 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6880 * @cfg {String} valign Vertical aligns the content in a table row
6883 * Create a new TableRow
6884 * @param {Object} config The config object
6887 Roo.bootstrap.TableRow = function(config){
6888 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6891 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
6899 getAutoCreate : function(){
6900 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6910 cfg.align = this.align;
6913 cfg.bgcolor = this.bgcolor;
6916 cfg.charoff = this.charoff;
6919 cfg.valign = this.valign;
6937 * @class Roo.bootstrap.TableBody
6938 * @extends Roo.bootstrap.Component
6939 * Bootstrap TableBody class
6940 * @cfg {String} cls element class
6941 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6942 * @cfg {String} align Aligns the content inside the element
6943 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6944 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6947 * Create a new TableBody
6948 * @param {Object} config The config object
6951 Roo.bootstrap.TableBody = function(config){
6952 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6955 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
6963 getAutoCreate : function(){
6964 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6978 cfg.align = this.align;
6981 cfg.charoff = this.charoff;
6984 cfg.valign = this.valign;
6991 // initEvents : function()
6998 // this.store = Roo.factory(this.store, Roo.data);
6999 // this.store.on('load', this.onLoad, this);
7001 // this.store.load();
7005 // onLoad: function ()
7007 // this.fireEvent('load', this);
7017 * Ext JS Library 1.1.1
7018 * Copyright(c) 2006-2007, Ext JS, LLC.
7020 * Originally Released Under LGPL - original licence link has changed is not relivant.
7023 * <script type="text/javascript">
7026 // as we use this in bootstrap.
7027 Roo.namespace('Roo.form');
7029 * @class Roo.form.Action
7030 * Internal Class used to handle form actions
7032 * @param {Roo.form.BasicForm} el The form element or its id
7033 * @param {Object} config Configuration options
7038 // define the action interface
7039 Roo.form.Action = function(form, options){
7041 this.options = options || {};
7044 * Client Validation Failed
7047 Roo.form.Action.CLIENT_INVALID = 'client';
7049 * Server Validation Failed
7052 Roo.form.Action.SERVER_INVALID = 'server';
7054 * Connect to Server Failed
7057 Roo.form.Action.CONNECT_FAILURE = 'connect';
7059 * Reading Data from Server Failed
7062 Roo.form.Action.LOAD_FAILURE = 'load';
7064 Roo.form.Action.prototype = {
7066 failureType : undefined,
7067 response : undefined,
7071 run : function(options){
7076 success : function(response){
7081 handleResponse : function(response){
7085 // default connection failure
7086 failure : function(response){
7088 this.response = response;
7089 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7090 this.form.afterAction(this, false);
7093 processResponse : function(response){
7094 this.response = response;
7095 if(!response.responseText){
7098 this.result = this.handleResponse(response);
7102 // utility functions used internally
7103 getUrl : function(appendParams){
7104 var url = this.options.url || this.form.url || this.form.el.dom.action;
7106 var p = this.getParams();
7108 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7114 getMethod : function(){
7115 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7118 getParams : function(){
7119 var bp = this.form.baseParams;
7120 var p = this.options.params;
7122 if(typeof p == "object"){
7123 p = Roo.urlEncode(Roo.applyIf(p, bp));
7124 }else if(typeof p == 'string' && bp){
7125 p += '&' + Roo.urlEncode(bp);
7128 p = Roo.urlEncode(bp);
7133 createCallback : function(){
7135 success: this.success,
7136 failure: this.failure,
7138 timeout: (this.form.timeout*1000),
7139 upload: this.form.fileUpload ? this.success : undefined
7144 Roo.form.Action.Submit = function(form, options){
7145 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7148 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7151 haveProgress : false,
7152 uploadComplete : false,
7154 // uploadProgress indicator.
7155 uploadProgress : function()
7157 if (!this.form.progressUrl) {
7161 if (!this.haveProgress) {
7162 Roo.MessageBox.progress("Uploading", "Uploading");
7164 if (this.uploadComplete) {
7165 Roo.MessageBox.hide();
7169 this.haveProgress = true;
7171 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7173 var c = new Roo.data.Connection();
7175 url : this.form.progressUrl,
7180 success : function(req){
7181 //console.log(data);
7185 rdata = Roo.decode(req.responseText)
7187 Roo.log("Invalid data from server..");
7191 if (!rdata || !rdata.success) {
7193 Roo.MessageBox.alert(Roo.encode(rdata));
7196 var data = rdata.data;
7198 if (this.uploadComplete) {
7199 Roo.MessageBox.hide();
7204 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7205 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7208 this.uploadProgress.defer(2000,this);
7211 failure: function(data) {
7212 Roo.log('progress url failed ');
7223 // run get Values on the form, so it syncs any secondary forms.
7224 this.form.getValues();
7226 var o = this.options;
7227 var method = this.getMethod();
7228 var isPost = method == 'POST';
7229 if(o.clientValidation === false || this.form.isValid()){
7231 if (this.form.progressUrl) {
7232 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7233 (new Date() * 1) + '' + Math.random());
7238 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7239 form:this.form.el.dom,
7240 url:this.getUrl(!isPost),
7242 params:isPost ? this.getParams() : null,
7243 isUpload: this.form.fileUpload
7246 this.uploadProgress();
7248 }else if (o.clientValidation !== false){ // client validation failed
7249 this.failureType = Roo.form.Action.CLIENT_INVALID;
7250 this.form.afterAction(this, false);
7254 success : function(response)
7256 this.uploadComplete= true;
7257 if (this.haveProgress) {
7258 Roo.MessageBox.hide();
7262 var result = this.processResponse(response);
7263 if(result === true || result.success){
7264 this.form.afterAction(this, true);
7268 this.form.markInvalid(result.errors);
7269 this.failureType = Roo.form.Action.SERVER_INVALID;
7271 this.form.afterAction(this, false);
7273 failure : function(response)
7275 this.uploadComplete= true;
7276 if (this.haveProgress) {
7277 Roo.MessageBox.hide();
7280 this.response = response;
7281 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7282 this.form.afterAction(this, false);
7285 handleResponse : function(response){
7286 if(this.form.errorReader){
7287 var rs = this.form.errorReader.read(response);
7290 for(var i = 0, len = rs.records.length; i < len; i++) {
7291 var r = rs.records[i];
7295 if(errors.length < 1){
7299 success : rs.success,
7305 ret = Roo.decode(response.responseText);
7309 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7319 Roo.form.Action.Load = function(form, options){
7320 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7321 this.reader = this.form.reader;
7324 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7329 Roo.Ajax.request(Roo.apply(
7330 this.createCallback(), {
7331 method:this.getMethod(),
7332 url:this.getUrl(false),
7333 params:this.getParams()
7337 success : function(response){
7339 var result = this.processResponse(response);
7340 if(result === true || !result.success || !result.data){
7341 this.failureType = Roo.form.Action.LOAD_FAILURE;
7342 this.form.afterAction(this, false);
7345 this.form.clearInvalid();
7346 this.form.setValues(result.data);
7347 this.form.afterAction(this, true);
7350 handleResponse : function(response){
7351 if(this.form.reader){
7352 var rs = this.form.reader.read(response);
7353 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7355 success : rs.success,
7359 return Roo.decode(response.responseText);
7363 Roo.form.Action.ACTION_TYPES = {
7364 'load' : Roo.form.Action.Load,
7365 'submit' : Roo.form.Action.Submit
7374 * @class Roo.bootstrap.Form
7375 * @extends Roo.bootstrap.Component
7376 * Bootstrap Form class
7377 * @cfg {String} method GET | POST (default POST)
7378 * @cfg {String} labelAlign top | left (default top)
7379 * @cfg {String} align left | right - for navbars
7380 * @cfg {Boolean} loadMask load mask when submit (default true)
7385 * @param {Object} config The config object
7389 Roo.bootstrap.Form = function(config){
7390 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7393 * @event clientvalidation
7394 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7395 * @param {Form} this
7396 * @param {Boolean} valid true if the form has passed client-side validation
7398 clientvalidation: true,
7400 * @event beforeaction
7401 * Fires before any action is performed. Return false to cancel the action.
7402 * @param {Form} this
7403 * @param {Action} action The action to be performed
7407 * @event actionfailed
7408 * Fires when an action fails.
7409 * @param {Form} this
7410 * @param {Action} action The action that failed
7412 actionfailed : true,
7414 * @event actioncomplete
7415 * Fires when an action is completed.
7416 * @param {Form} this
7417 * @param {Action} action The action that completed
7419 actioncomplete : true
7424 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7427 * @cfg {String} method
7428 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7433 * The URL to use for form actions if one isn't supplied in the action options.
7436 * @cfg {Boolean} fileUpload
7437 * Set to true if this form is a file upload.
7441 * @cfg {Object} baseParams
7442 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7446 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7450 * @cfg {Sting} align (left|right) for navbar forms
7455 activeAction : null,
7458 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7459 * element by passing it or its id or mask the form itself by passing in true.
7462 waitMsgTarget : false,
7466 getAutoCreate : function(){
7470 method : this.method || 'POST',
7471 id : this.id || Roo.id(),
7474 if (this.parent().xtype.match(/^Nav/)) {
7475 cfg.cls = 'navbar-form navbar-' + this.align;
7479 if (this.labelAlign == 'left' ) {
7480 cfg.cls += ' form-horizontal';
7486 initEvents : function()
7488 this.el.on('submit', this.onSubmit, this);
7489 // this was added as random key presses on the form where triggering form submit.
7490 this.el.on('keypress', function(e) {
7491 if (e.getCharCode() != 13) {
7494 // we might need to allow it for textareas.. and some other items.
7495 // check e.getTarget().
7497 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7501 Roo.log("keypress blocked");
7509 onSubmit : function(e){
7514 * Returns true if client-side validation on the form is successful.
7517 isValid : function(){
7518 var items = this.getItems();
7520 items.each(function(f){
7529 * Returns true if any fields in this form have changed since their original load.
7532 isDirty : function(){
7534 var items = this.getItems();
7535 items.each(function(f){
7545 * Performs a predefined action (submit or load) or custom actions you define on this form.
7546 * @param {String} actionName The name of the action type
7547 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7548 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7549 * accept other config options):
7551 Property Type Description
7552 ---------------- --------------- ----------------------------------------------------------------------------------
7553 url String The url for the action (defaults to the form's url)
7554 method String The form method to use (defaults to the form's method, or POST if not defined)
7555 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7556 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7557 validate the form on the client (defaults to false)
7559 * @return {BasicForm} this
7561 doAction : function(action, options){
7562 if(typeof action == 'string'){
7563 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7565 if(this.fireEvent('beforeaction', this, action) !== false){
7566 this.beforeAction(action);
7567 action.run.defer(100, action);
7573 beforeAction : function(action){
7574 var o = action.options;
7577 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7579 // not really supported yet.. ??
7581 //if(this.waitMsgTarget === true){
7582 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7583 //}else if(this.waitMsgTarget){
7584 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7585 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7587 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7593 afterAction : function(action, success){
7594 this.activeAction = null;
7595 var o = action.options;
7597 //if(this.waitMsgTarget === true){
7599 //}else if(this.waitMsgTarget){
7600 // this.waitMsgTarget.unmask();
7602 // Roo.MessageBox.updateProgress(1);
7603 // Roo.MessageBox.hide();
7610 Roo.callback(o.success, o.scope, [this, action]);
7611 this.fireEvent('actioncomplete', this, action);
7615 // failure condition..
7616 // we have a scenario where updates need confirming.
7617 // eg. if a locking scenario exists..
7618 // we look for { errors : { needs_confirm : true }} in the response.
7620 (typeof(action.result) != 'undefined') &&
7621 (typeof(action.result.errors) != 'undefined') &&
7622 (typeof(action.result.errors.needs_confirm) != 'undefined')
7625 Roo.log("not supported yet");
7628 Roo.MessageBox.confirm(
7629 "Change requires confirmation",
7630 action.result.errorMsg,
7635 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7645 Roo.callback(o.failure, o.scope, [this, action]);
7646 // show an error message if no failed handler is set..
7647 if (!this.hasListener('actionfailed')) {
7648 Roo.log("need to add dialog support");
7650 Roo.MessageBox.alert("Error",
7651 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7652 action.result.errorMsg :
7653 "Saving Failed, please check your entries or try again"
7658 this.fireEvent('actionfailed', this, action);
7663 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7664 * @param {String} id The value to search for
7667 findField : function(id){
7668 var items = this.getItems();
7669 var field = items.get(id);
7671 items.each(function(f){
7672 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7679 return field || null;
7682 * Mark fields in this form invalid in bulk.
7683 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7684 * @return {BasicForm} this
7686 markInvalid : function(errors){
7687 if(errors instanceof Array){
7688 for(var i = 0, len = errors.length; i < len; i++){
7689 var fieldError = errors[i];
7690 var f = this.findField(fieldError.id);
7692 f.markInvalid(fieldError.msg);
7698 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7699 field.markInvalid(errors[id]);
7703 //Roo.each(this.childForms || [], function (f) {
7704 // f.markInvalid(errors);
7711 * Set values for fields in this form in bulk.
7712 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7713 * @return {BasicForm} this
7715 setValues : function(values){
7716 if(values instanceof Array){ // array of objects
7717 for(var i = 0, len = values.length; i < len; i++){
7719 var f = this.findField(v.id);
7721 f.setValue(v.value);
7722 if(this.trackResetOnLoad){
7723 f.originalValue = f.getValue();
7727 }else{ // object hash
7730 if(typeof values[id] != 'function' && (field = this.findField(id))){
7732 if (field.setFromData &&
7734 field.displayField &&
7735 // combos' with local stores can
7736 // be queried via setValue()
7737 // to set their value..
7738 (field.store && !field.store.isLocal)
7742 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7743 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7744 field.setFromData(sd);
7747 field.setValue(values[id]);
7751 if(this.trackResetOnLoad){
7752 field.originalValue = field.getValue();
7758 //Roo.each(this.childForms || [], function (f) {
7759 // f.setValues(values);
7766 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7767 * they are returned as an array.
7768 * @param {Boolean} asString
7771 getValues : function(asString){
7772 //if (this.childForms) {
7773 // copy values from the child forms
7774 // Roo.each(this.childForms, function (f) {
7775 // this.setValues(f.getValues());
7781 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7782 if(asString === true){
7785 return Roo.urlDecode(fs);
7789 * Returns the fields in this form as an object with key/value pairs.
7790 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7793 getFieldValues : function(with_hidden)
7795 var items = this.getItems();
7797 items.each(function(f){
7801 var v = f.getValue();
7802 if (f.inputType =='radio') {
7803 if (typeof(ret[f.getName()]) == 'undefined') {
7804 ret[f.getName()] = ''; // empty..
7807 if (!f.el.dom.checked) {
7815 // not sure if this supported any more..
7816 if ((typeof(v) == 'object') && f.getRawValue) {
7817 v = f.getRawValue() ; // dates..
7819 // combo boxes where name != hiddenName...
7820 if (f.name != f.getName()) {
7821 ret[f.name] = f.getRawValue();
7823 ret[f.getName()] = v;
7830 * Clears all invalid messages in this form.
7831 * @return {BasicForm} this
7833 clearInvalid : function(){
7834 var items = this.getItems();
7836 items.each(function(f){
7847 * @return {BasicForm} this
7850 var items = this.getItems();
7851 items.each(function(f){
7855 Roo.each(this.childForms || [], function (f) {
7862 getItems : function()
7864 var r=new Roo.util.MixedCollection(false, function(o){
7865 return o.id || (o.id = Roo.id());
7867 var iter = function(el) {
7874 Roo.each(el.items,function(e) {
7894 * Ext JS Library 1.1.1
7895 * Copyright(c) 2006-2007, Ext JS, LLC.
7897 * Originally Released Under LGPL - original licence link has changed is not relivant.
7900 * <script type="text/javascript">
7903 * @class Roo.form.VTypes
7904 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7907 Roo.form.VTypes = function(){
7908 // closure these in so they are only created once.
7909 var alpha = /^[a-zA-Z_]+$/;
7910 var alphanum = /^[a-zA-Z0-9_]+$/;
7911 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7912 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7914 // All these messages and functions are configurable
7917 * The function used to validate email addresses
7918 * @param {String} value The email address
7920 'email' : function(v){
7921 return email.test(v);
7924 * The error text to display when the email validation function returns false
7927 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7929 * The keystroke filter mask to be applied on email input
7932 'emailMask' : /[a-z0-9_\.\-@]/i,
7935 * The function used to validate URLs
7936 * @param {String} value The URL
7938 'url' : function(v){
7942 * The error text to display when the url validation function returns false
7945 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7948 * The function used to validate alpha values
7949 * @param {String} value The value
7951 'alpha' : function(v){
7952 return alpha.test(v);
7955 * The error text to display when the alpha validation function returns false
7958 'alphaText' : 'This field should only contain letters and _',
7960 * The keystroke filter mask to be applied on alpha input
7963 'alphaMask' : /[a-z_]/i,
7966 * The function used to validate alphanumeric values
7967 * @param {String} value The value
7969 'alphanum' : function(v){
7970 return alphanum.test(v);
7973 * The error text to display when the alphanumeric validation function returns false
7976 'alphanumText' : 'This field should only contain letters, numbers and _',
7978 * The keystroke filter mask to be applied on alphanumeric input
7981 'alphanumMask' : /[a-z0-9_]/i
7991 * @class Roo.bootstrap.Input
7992 * @extends Roo.bootstrap.Component
7993 * Bootstrap Input class
7994 * @cfg {Boolean} disabled is it disabled
7995 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7996 * @cfg {String} name name of the input
7997 * @cfg {string} fieldLabel - the label associated
7998 * @cfg {string} placeholder - placeholder to put in text.
7999 * @cfg {string} before - input group add on before
8000 * @cfg {string} after - input group add on after
8001 * @cfg {string} size - (lg|sm) or leave empty..
8002 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8003 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8004 * @cfg {Number} md colspan out of 12 for computer-sized screens
8005 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8006 * @cfg {string} value default value of the input
8007 * @cfg {Number} labelWidth set the width of label (0-12)
8008 * @cfg {String} labelAlign (top|left)
8009 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8010 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8012 * @cfg {String} align (left|center|right) Default left
8013 * @cfg {Boolean} forceFeedback (true|false) Default false
8019 * Create a new Input
8020 * @param {Object} config The config object
8023 Roo.bootstrap.Input = function(config){
8024 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8029 * Fires when this field receives input focus.
8030 * @param {Roo.form.Field} this
8035 * Fires when this field loses input focus.
8036 * @param {Roo.form.Field} this
8041 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8042 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8043 * @param {Roo.form.Field} this
8044 * @param {Roo.EventObject} e The event object
8049 * Fires just before the field blurs if the field value has changed.
8050 * @param {Roo.form.Field} this
8051 * @param {Mixed} newValue The new value
8052 * @param {Mixed} oldValue The original value
8057 * Fires after the field has been marked as invalid.
8058 * @param {Roo.form.Field} this
8059 * @param {String} msg The validation message
8064 * Fires after the field has been validated with no errors.
8065 * @param {Roo.form.Field} this
8070 * Fires after the key up
8071 * @param {Roo.form.Field} this
8072 * @param {Roo.EventObject} e The event Object
8078 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8080 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8081 automatic validation (defaults to "keyup").
8083 validationEvent : "keyup",
8085 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8087 validateOnBlur : true,
8089 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8091 validationDelay : 250,
8093 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8095 focusClass : "x-form-focus", // not needed???
8099 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8101 invalidClass : "has-warning",
8104 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8106 validClass : "has-success",
8109 * @cfg {Boolean} hasFeedback (true|false) default true
8114 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8116 invalidFeedbackClass : "glyphicon-warning-sign",
8119 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8121 validFeedbackClass : "glyphicon-ok",
8124 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8126 selectOnFocus : false,
8129 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8133 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8138 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8140 disableKeyFilter : false,
8143 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8147 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8151 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8153 blankText : "This field is required",
8156 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8160 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8162 maxLength : Number.MAX_VALUE,
8164 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8166 minLengthText : "The minimum length for this field is {0}",
8168 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8170 maxLengthText : "The maximum length for this field is {0}",
8174 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8175 * If available, this function will be called only after the basic validators all return true, and will be passed the
8176 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8180 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8181 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8182 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8186 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8190 autocomplete: false,
8209 formatedValue : false,
8210 forceFeedback : false,
8212 parentLabelAlign : function()
8215 while (parent.parent()) {
8216 parent = parent.parent();
8217 if (typeof(parent.labelAlign) !='undefined') {
8218 return parent.labelAlign;
8225 getAutoCreate : function(){
8227 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8235 if(this.inputType != 'hidden'){
8236 cfg.cls = 'form-group' //input-group
8242 type : this.inputType,
8244 cls : 'form-control',
8245 placeholder : this.placeholder || '',
8246 autocomplete : this.autocomplete || 'new-password'
8251 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8254 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8255 input.maxLength = this.maxLength;
8258 if (this.disabled) {
8259 input.disabled=true;
8262 if (this.readOnly) {
8263 input.readonly=true;
8267 input.name = this.name;
8270 input.cls += ' input-' + this.size;
8273 ['xs','sm','md','lg'].map(function(size){
8274 if (settings[size]) {
8275 cfg.cls += ' col-' + size + '-' + settings[size];
8279 var inputblock = input;
8283 cls: 'glyphicon form-control-feedback'
8286 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8289 cls : 'has-feedback',
8297 if (this.before || this.after) {
8300 cls : 'input-group',
8304 if (this.before && typeof(this.before) == 'string') {
8306 inputblock.cn.push({
8308 cls : 'roo-input-before input-group-addon',
8312 if (this.before && typeof(this.before) == 'object') {
8313 this.before = Roo.factory(this.before);
8315 inputblock.cn.push({
8317 cls : 'roo-input-before input-group-' +
8318 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8322 inputblock.cn.push(input);
8324 if (this.after && typeof(this.after) == 'string') {
8325 inputblock.cn.push({
8327 cls : 'roo-input-after input-group-addon',
8331 if (this.after && typeof(this.after) == 'object') {
8332 this.after = Roo.factory(this.after);
8334 inputblock.cn.push({
8336 cls : 'roo-input-after input-group-' +
8337 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8341 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8342 inputblock.cls += ' has-feedback';
8343 inputblock.cn.push(feedback);
8347 if (align ==='left' && this.fieldLabel.length) {
8354 cls : 'control-label col-sm-' + this.labelWidth,
8355 html : this.fieldLabel
8359 cls : "col-sm-" + (12 - this.labelWidth),
8366 } else if ( this.fieldLabel.length) {
8372 //cls : 'input-group-addon',
8373 html : this.fieldLabel
8392 if (this.parentType === 'Navbar' && this.parent().bar) {
8393 cfg.cls += ' navbar-form';
8395 if (this.parentType === 'NavGroup') {
8396 cfg.cls += ' navbar-form';
8403 * return the real input element.
8405 inputEl: function ()
8407 return this.el.select('input.form-control',true).first();
8410 tooltipEl : function()
8412 return this.inputEl();
8415 setDisabled : function(v)
8417 var i = this.inputEl().dom;
8419 i.removeAttribute('disabled');
8423 i.setAttribute('disabled','true');
8425 initEvents : function()
8428 this.inputEl().on("keydown" , this.fireKey, this);
8429 this.inputEl().on("focus", this.onFocus, this);
8430 this.inputEl().on("blur", this.onBlur, this);
8432 this.inputEl().relayEvent('keyup', this);
8434 // reference to original value for reset
8435 this.originalValue = this.getValue();
8436 //Roo.form.TextField.superclass.initEvents.call(this);
8437 if(this.validationEvent == 'keyup'){
8438 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8439 this.inputEl().on('keyup', this.filterValidation, this);
8441 else if(this.validationEvent !== false){
8442 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8445 if(this.selectOnFocus){
8446 this.on("focus", this.preFocus, this);
8449 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8450 this.inputEl().on("keypress", this.filterKeys, this);
8453 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8454 this.el.on("click", this.autoSize, this);
8457 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8458 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8461 if (typeof(this.before) == 'object') {
8462 this.before.render(this.el.select('.roo-input-before',true).first());
8464 if (typeof(this.after) == 'object') {
8465 this.after.render(this.el.select('.roo-input-after',true).first());
8470 filterValidation : function(e){
8471 if(!e.isNavKeyPress()){
8472 this.validationTask.delay(this.validationDelay);
8476 * Validates the field value
8477 * @return {Boolean} True if the value is valid, else false
8479 validate : function(){
8480 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8481 if(this.disabled || this.validateValue(this.getRawValue())){
8492 * Validates a value according to the field's validation rules and marks the field as invalid
8493 * if the validation fails
8494 * @param {Mixed} value The value to validate
8495 * @return {Boolean} True if the value is valid, else false
8497 validateValue : function(value){
8498 if(value.length < 1) { // if it's blank
8499 if(this.allowBlank){
8505 if(value.length < this.minLength){
8508 if(value.length > this.maxLength){
8512 var vt = Roo.form.VTypes;
8513 if(!vt[this.vtype](value, this)){
8517 if(typeof this.validator == "function"){
8518 var msg = this.validator(value);
8524 if(this.regex && !this.regex.test(value)){
8534 fireKey : function(e){
8535 //Roo.log('field ' + e.getKey());
8536 if(e.isNavKeyPress()){
8537 this.fireEvent("specialkey", this, e);
8540 focus : function (selectText){
8542 this.inputEl().focus();
8543 if(selectText === true){
8544 this.inputEl().dom.select();
8550 onFocus : function(){
8551 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8552 // this.el.addClass(this.focusClass);
8555 this.hasFocus = true;
8556 this.startValue = this.getValue();
8557 this.fireEvent("focus", this);
8561 beforeBlur : Roo.emptyFn,
8565 onBlur : function(){
8567 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8568 //this.el.removeClass(this.focusClass);
8570 this.hasFocus = false;
8571 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8574 var v = this.getValue();
8575 if(String(v) !== String(this.startValue)){
8576 this.fireEvent('change', this, v, this.startValue);
8578 this.fireEvent("blur", this);
8582 * Resets the current field value to the originally loaded value and clears any validation messages
8585 this.setValue(this.originalValue);
8589 * Returns the name of the field
8590 * @return {Mixed} name The name field
8592 getName: function(){
8596 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8597 * @return {Mixed} value The field value
8599 getValue : function(){
8601 var v = this.inputEl().getValue();
8606 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8607 * @return {Mixed} value The field value
8609 getRawValue : function(){
8610 var v = this.inputEl().getValue();
8616 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8617 * @param {Mixed} value The value to set
8619 setRawValue : function(v){
8620 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8623 selectText : function(start, end){
8624 var v = this.getRawValue();
8626 start = start === undefined ? 0 : start;
8627 end = end === undefined ? v.length : end;
8628 var d = this.inputEl().dom;
8629 if(d.setSelectionRange){
8630 d.setSelectionRange(start, end);
8631 }else if(d.createTextRange){
8632 var range = d.createTextRange();
8633 range.moveStart("character", start);
8634 range.moveEnd("character", v.length-end);
8641 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8642 * @param {Mixed} value The value to set
8644 setValue : function(v){
8647 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8653 processValue : function(value){
8654 if(this.stripCharsRe){
8655 var newValue = value.replace(this.stripCharsRe, '');
8656 if(newValue !== value){
8657 this.setRawValue(newValue);
8664 preFocus : function(){
8666 if(this.selectOnFocus){
8667 this.inputEl().dom.select();
8670 filterKeys : function(e){
8672 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8675 var c = e.getCharCode(), cc = String.fromCharCode(c);
8676 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8679 if(!this.maskRe.test(cc)){
8684 * Clear any invalid styles/messages for this field
8686 clearInvalid : function(){
8688 if(!this.el || this.preventMark){ // not rendered
8692 var label = this.el.select('label', true).first();
8693 var icon = this.el.select('i.fa-star', true).first();
8699 this.el.removeClass(this.invalidClass);
8701 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8703 var feedback = this.el.select('.form-control-feedback', true).first();
8706 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8711 this.fireEvent('valid', this);
8715 * Mark this field as valid
8717 markValid : function()
8719 if(!this.el || this.preventMark){ // not rendered
8723 this.el.removeClass([this.invalidClass, this.validClass]);
8725 var feedback = this.el.select('.form-control-feedback', true).first();
8728 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8731 if(this.disabled || this.allowBlank){
8735 var formGroup = this.el.findParent('.form-group', false, true);
8739 var label = formGroup.select('label', true).first();
8740 var icon = formGroup.select('i.fa-star', true).first();
8747 this.el.addClass(this.validClass);
8749 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8751 var feedback = this.el.select('.form-control-feedback', true).first();
8754 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8755 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8760 this.fireEvent('valid', this);
8764 * Mark this field as invalid
8765 * @param {String} msg The validation message
8767 markInvalid : function(msg)
8769 if(!this.el || this.preventMark){ // not rendered
8773 this.el.removeClass([this.invalidClass, this.validClass]);
8775 var feedback = this.el.select('.form-control-feedback', true).first();
8778 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8781 if(this.disabled || this.allowBlank){
8785 var formGroup = this.el.findParent('.form-group', false, true);
8788 var label = formGroup.select('label', true).first();
8789 var icon = formGroup.select('i.fa-star', true).first();
8791 if(!this.getValue().length && label && !icon){
8792 this.el.findParent('.form-group', false, true).createChild({
8794 cls : 'text-danger fa fa-lg fa-star',
8795 tooltip : 'This field is required',
8796 style : 'margin-right:5px;'
8802 this.el.addClass(this.invalidClass);
8804 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8806 var feedback = this.el.select('.form-control-feedback', true).first();
8809 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8811 if(this.getValue().length || this.forceFeedback){
8812 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8819 this.fireEvent('invalid', this, msg);
8822 SafariOnKeyDown : function(event)
8824 // this is a workaround for a password hang bug on chrome/ webkit.
8826 var isSelectAll = false;
8828 if(this.inputEl().dom.selectionEnd > 0){
8829 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8831 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8832 event.preventDefault();
8837 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
8839 event.preventDefault();
8840 // this is very hacky as keydown always get's upper case.
8842 var cc = String.fromCharCode(event.getCharCode());
8843 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
8847 adjustWidth : function(tag, w){
8848 tag = tag.toLowerCase();
8849 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8850 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8854 if(tag == 'textarea'){
8857 }else if(Roo.isOpera){
8861 if(tag == 'textarea'){
8880 * @class Roo.bootstrap.TextArea
8881 * @extends Roo.bootstrap.Input
8882 * Bootstrap TextArea class
8883 * @cfg {Number} cols Specifies the visible width of a text area
8884 * @cfg {Number} rows Specifies the visible number of lines in a text area
8885 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8886 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8887 * @cfg {string} html text
8890 * Create a new TextArea
8891 * @param {Object} config The config object
8894 Roo.bootstrap.TextArea = function(config){
8895 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8899 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
8909 getAutoCreate : function(){
8911 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8922 value : this.value || '',
8923 html: this.html || '',
8924 cls : 'form-control',
8925 placeholder : this.placeholder || ''
8929 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8930 input.maxLength = this.maxLength;
8934 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8938 input.cols = this.cols;
8941 if (this.readOnly) {
8942 input.readonly = true;
8946 input.name = this.name;
8950 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8954 ['xs','sm','md','lg'].map(function(size){
8955 if (settings[size]) {
8956 cfg.cls += ' col-' + size + '-' + settings[size];
8960 var inputblock = input;
8962 if(this.hasFeedback && !this.allowBlank){
8966 cls: 'glyphicon form-control-feedback'
8970 cls : 'has-feedback',
8979 if (this.before || this.after) {
8982 cls : 'input-group',
8986 inputblock.cn.push({
8988 cls : 'input-group-addon',
8993 inputblock.cn.push(input);
8995 if(this.hasFeedback && !this.allowBlank){
8996 inputblock.cls += ' has-feedback';
8997 inputblock.cn.push(feedback);
9001 inputblock.cn.push({
9003 cls : 'input-group-addon',
9010 if (align ==='left' && this.fieldLabel.length) {
9011 // Roo.log("left and has label");
9017 cls : 'control-label col-sm-' + this.labelWidth,
9018 html : this.fieldLabel
9022 cls : "col-sm-" + (12 - this.labelWidth),
9029 } else if ( this.fieldLabel.length) {
9030 // Roo.log(" label");
9035 //cls : 'input-group-addon',
9036 html : this.fieldLabel
9046 // Roo.log(" no label && no align");
9056 if (this.disabled) {
9057 input.disabled=true;
9064 * return the real textarea element.
9066 inputEl: function ()
9068 return this.el.select('textarea.form-control',true).first();
9072 * Clear any invalid styles/messages for this field
9074 clearInvalid : function()
9077 if(!this.el || this.preventMark){ // not rendered
9081 var label = this.el.select('label', true).first();
9082 var icon = this.el.select('i.fa-star', true).first();
9088 this.el.removeClass(this.invalidClass);
9090 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9092 var feedback = this.el.select('.form-control-feedback', true).first();
9095 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9100 this.fireEvent('valid', this);
9104 * Mark this field as valid
9106 markValid : function()
9108 if(!this.el || this.preventMark){ // not rendered
9112 this.el.removeClass([this.invalidClass, this.validClass]);
9114 var feedback = this.el.select('.form-control-feedback', true).first();
9117 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9120 if(this.disabled || this.allowBlank){
9124 var label = this.el.select('label', true).first();
9125 var icon = this.el.select('i.fa-star', true).first();
9131 this.el.addClass(this.validClass);
9133 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9135 var feedback = this.el.select('.form-control-feedback', true).first();
9138 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9139 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9144 this.fireEvent('valid', this);
9148 * Mark this field as invalid
9149 * @param {String} msg The validation message
9151 markInvalid : function(msg)
9153 if(!this.el || this.preventMark){ // not rendered
9157 this.el.removeClass([this.invalidClass, this.validClass]);
9159 var feedback = this.el.select('.form-control-feedback', true).first();
9162 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9165 if(this.disabled || this.allowBlank){
9169 var label = this.el.select('label', true).first();
9170 var icon = this.el.select('i.fa-star', true).first();
9172 if(!this.getValue().length && label && !icon){
9173 this.el.createChild({
9175 cls : 'text-danger fa fa-lg fa-star',
9176 tooltip : 'This field is required',
9177 style : 'margin-right:5px;'
9181 this.el.addClass(this.invalidClass);
9183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9185 var feedback = this.el.select('.form-control-feedback', true).first();
9188 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9190 if(this.getValue().length || this.forceFeedback){
9191 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9198 this.fireEvent('invalid', this, msg);
9206 * trigger field - base class for combo..
9211 * @class Roo.bootstrap.TriggerField
9212 * @extends Roo.bootstrap.Input
9213 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9214 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9215 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9216 * for which you can provide a custom implementation. For example:
9218 var trigger = new Roo.bootstrap.TriggerField();
9219 trigger.onTriggerClick = myTriggerFn;
9220 trigger.applyTo('my-field');
9223 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9224 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9225 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9226 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9227 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9230 * Create a new TriggerField.
9231 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9232 * to the base TextField)
9234 Roo.bootstrap.TriggerField = function(config){
9235 this.mimicing = false;
9236 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9239 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9241 * @cfg {String} triggerClass A CSS class to apply to the trigger
9244 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9249 * @cfg {Boolean} removable (true|false) special filter default false
9253 /** @cfg {Boolean} grow @hide */
9254 /** @cfg {Number} growMin @hide */
9255 /** @cfg {Number} growMax @hide */
9261 autoSize: Roo.emptyFn,
9268 actionMode : 'wrap',
9273 getAutoCreate : function(){
9275 var align = this.labelAlign || this.parentLabelAlign();
9280 cls: 'form-group' //input-group
9287 type : this.inputType,
9288 cls : 'form-control',
9289 autocomplete: 'new-password',
9290 placeholder : this.placeholder || ''
9294 input.name = this.name;
9297 input.cls += ' input-' + this.size;
9300 if (this.disabled) {
9301 input.disabled=true;
9304 var inputblock = input;
9306 if(this.hasFeedback && !this.allowBlank){
9310 cls: 'glyphicon form-control-feedback'
9313 if(this.removable && !this.editable && !this.tickable){
9315 cls : 'has-feedback',
9321 cls : 'roo-combo-removable-btn close'
9328 cls : 'has-feedback',
9337 if(this.removable && !this.editable && !this.tickable){
9339 cls : 'roo-removable',
9345 cls : 'roo-combo-removable-btn close'
9352 if (this.before || this.after) {
9355 cls : 'input-group',
9359 inputblock.cn.push({
9361 cls : 'input-group-addon',
9366 inputblock.cn.push(input);
9368 if(this.hasFeedback && !this.allowBlank){
9369 inputblock.cls += ' has-feedback';
9370 inputblock.cn.push(feedback);
9374 inputblock.cn.push({
9376 cls : 'input-group-addon',
9389 cls: 'form-hidden-field'
9403 cls: 'form-hidden-field'
9407 cls: 'roo-select2-choices',
9411 cls: 'roo-select2-search-field',
9424 cls: 'roo-select2-container input-group',
9429 // cls: 'typeahead typeahead-long dropdown-menu',
9430 // style: 'display:none'
9435 if(!this.multiple && this.showToggleBtn){
9441 if (this.caret != false) {
9444 cls: 'fa fa-' + this.caret
9451 cls : 'input-group-addon btn dropdown-toggle',
9456 cls: 'combobox-clear',
9470 combobox.cls += ' roo-select2-container-multi';
9473 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9475 // Roo.log("left and has label");
9481 cls : 'control-label col-sm-' + this.labelWidth,
9482 html : this.fieldLabel
9486 cls : "col-sm-" + (12 - this.labelWidth),
9493 } else if ( this.fieldLabel.length) {
9494 // Roo.log(" label");
9499 //cls : 'input-group-addon',
9500 html : this.fieldLabel
9510 // Roo.log(" no label && no align");
9517 ['xs','sm','md','lg'].map(function(size){
9518 if (settings[size]) {
9519 cfg.cls += ' col-' + size + '-' + settings[size];
9530 onResize : function(w, h){
9531 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9532 // if(typeof w == 'number'){
9533 // var x = w - this.trigger.getWidth();
9534 // this.inputEl().setWidth(this.adjustWidth('input', x));
9535 // this.trigger.setStyle('left', x+'px');
9540 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9543 getResizeEl : function(){
9544 return this.inputEl();
9548 getPositionEl : function(){
9549 return this.inputEl();
9553 alignErrorIcon : function(){
9554 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9558 initEvents : function(){
9562 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9563 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9564 if(!this.multiple && this.showToggleBtn){
9565 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9566 if(this.hideTrigger){
9567 this.trigger.setDisplayed(false);
9569 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9573 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9576 if(this.removable && !this.editable && !this.tickable){
9577 var close = this.closeTriggerEl();
9580 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9581 close.on('click', this.removeBtnClick, this, close);
9585 //this.trigger.addClassOnOver('x-form-trigger-over');
9586 //this.trigger.addClassOnClick('x-form-trigger-click');
9589 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9593 closeTriggerEl : function()
9595 var close = this.el.select('.roo-combo-removable-btn', true).first();
9596 return close ? close : false;
9599 removeBtnClick : function(e, h, el)
9603 if(this.fireEvent("remove", this) !== false){
9605 this.fireEvent("afterremove", this)
9609 createList : function()
9611 this.list = Roo.get(document.body).createChild({
9613 cls: 'typeahead typeahead-long dropdown-menu',
9614 style: 'display:none'
9617 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9622 initTrigger : function(){
9627 onDestroy : function(){
9629 this.trigger.removeAllListeners();
9630 // this.trigger.remove();
9633 // this.wrap.remove();
9635 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9639 onFocus : function(){
9640 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9643 this.wrap.addClass('x-trigger-wrap-focus');
9644 this.mimicing = true;
9645 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9646 if(this.monitorTab){
9647 this.el.on("keydown", this.checkTab, this);
9654 checkTab : function(e){
9655 if(e.getKey() == e.TAB){
9661 onBlur : function(){
9666 mimicBlur : function(e, t){
9668 if(!this.wrap.contains(t) && this.validateBlur()){
9675 triggerBlur : function(){
9676 this.mimicing = false;
9677 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9678 if(this.monitorTab){
9679 this.el.un("keydown", this.checkTab, this);
9681 //this.wrap.removeClass('x-trigger-wrap-focus');
9682 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9686 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9687 validateBlur : function(e, t){
9692 onDisable : function(){
9693 this.inputEl().dom.disabled = true;
9694 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9696 // this.wrap.addClass('x-item-disabled');
9701 onEnable : function(){
9702 this.inputEl().dom.disabled = false;
9703 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9705 // this.el.removeClass('x-item-disabled');
9710 onShow : function(){
9711 var ae = this.getActionEl();
9714 ae.dom.style.display = '';
9715 ae.dom.style.visibility = 'visible';
9721 onHide : function(){
9722 var ae = this.getActionEl();
9723 ae.dom.style.display = 'none';
9727 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9728 * by an implementing function.
9730 * @param {EventObject} e
9732 onTriggerClick : Roo.emptyFn
9736 * Ext JS Library 1.1.1
9737 * Copyright(c) 2006-2007, Ext JS, LLC.
9739 * Originally Released Under LGPL - original licence link has changed is not relivant.
9742 * <script type="text/javascript">
9747 * @class Roo.data.SortTypes
9749 * Defines the default sorting (casting?) comparison functions used when sorting data.
9751 Roo.data.SortTypes = {
9753 * Default sort that does nothing
9754 * @param {Mixed} s The value being converted
9755 * @return {Mixed} The comparison value
9762 * The regular expression used to strip tags
9766 stripTagsRE : /<\/?[^>]+>/gi,
9769 * Strips all HTML tags to sort on text only
9770 * @param {Mixed} s The value being converted
9771 * @return {String} The comparison value
9773 asText : function(s){
9774 return String(s).replace(this.stripTagsRE, "");
9778 * Strips all HTML tags to sort on text only - Case insensitive
9779 * @param {Mixed} s The value being converted
9780 * @return {String} The comparison value
9782 asUCText : function(s){
9783 return String(s).toUpperCase().replace(this.stripTagsRE, "");
9787 * Case insensitive string
9788 * @param {Mixed} s The value being converted
9789 * @return {String} The comparison value
9791 asUCString : function(s) {
9792 return String(s).toUpperCase();
9797 * @param {Mixed} s The value being converted
9798 * @return {Number} The comparison value
9800 asDate : function(s) {
9804 if(s instanceof Date){
9807 return Date.parse(String(s));
9812 * @param {Mixed} s The value being converted
9813 * @return {Float} The comparison value
9815 asFloat : function(s) {
9816 var val = parseFloat(String(s).replace(/,/g, ""));
9825 * @param {Mixed} s The value being converted
9826 * @return {Number} The comparison value
9828 asInt : function(s) {
9829 var val = parseInt(String(s).replace(/,/g, ""));
9837 * Ext JS Library 1.1.1
9838 * Copyright(c) 2006-2007, Ext JS, LLC.
9840 * Originally Released Under LGPL - original licence link has changed is not relivant.
9843 * <script type="text/javascript">
9847 * @class Roo.data.Record
9848 * Instances of this class encapsulate both record <em>definition</em> information, and record
9849 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9850 * to access Records cached in an {@link Roo.data.Store} object.<br>
9852 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9853 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9856 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9858 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9859 * {@link #create}. The parameters are the same.
9860 * @param {Array} data An associative Array of data values keyed by the field name.
9861 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9862 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9863 * not specified an integer id is generated.
9865 Roo.data.Record = function(data, id){
9866 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9871 * Generate a constructor for a specific record layout.
9872 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9873 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9874 * Each field definition object may contain the following properties: <ul>
9875 * <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,
9876 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9877 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9878 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9879 * is being used, then this is a string containing the javascript expression to reference the data relative to
9880 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9881 * to the data item relative to the record element. If the mapping expression is the same as the field name,
9882 * this may be omitted.</p></li>
9883 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9884 * <ul><li>auto (Default, implies no conversion)</li>
9889 * <li>date</li></ul></p></li>
9890 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9891 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9892 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9893 * by the Reader into an object that will be stored in the Record. It is passed the
9894 * following parameters:<ul>
9895 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9897 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9899 * <br>usage:<br><pre><code>
9900 var TopicRecord = Roo.data.Record.create(
9901 {name: 'title', mapping: 'topic_title'},
9902 {name: 'author', mapping: 'username'},
9903 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9904 {name: 'lastPost', mapping: 'post_time', type: 'date'},
9905 {name: 'lastPoster', mapping: 'user2'},
9906 {name: 'excerpt', mapping: 'post_text'}
9909 var myNewRecord = new TopicRecord({
9910 title: 'Do my job please',
9913 lastPost: new Date(),
9914 lastPoster: 'Animal',
9915 excerpt: 'No way dude!'
9917 myStore.add(myNewRecord);
9922 Roo.data.Record.create = function(o){
9924 f.superclass.constructor.apply(this, arguments);
9926 Roo.extend(f, Roo.data.Record);
9927 var p = f.prototype;
9928 p.fields = new Roo.util.MixedCollection(false, function(field){
9931 for(var i = 0, len = o.length; i < len; i++){
9932 p.fields.add(new Roo.data.Field(o[i]));
9934 f.getField = function(name){
9935 return p.fields.get(name);
9940 Roo.data.Record.AUTO_ID = 1000;
9941 Roo.data.Record.EDIT = 'edit';
9942 Roo.data.Record.REJECT = 'reject';
9943 Roo.data.Record.COMMIT = 'commit';
9945 Roo.data.Record.prototype = {
9947 * Readonly flag - true if this record has been modified.
9956 join : function(store){
9961 * Set the named field to the specified value.
9962 * @param {String} name The name of the field to set.
9963 * @param {Object} value The value to set the field to.
9965 set : function(name, value){
9966 if(this.data[name] == value){
9973 if(typeof this.modified[name] == 'undefined'){
9974 this.modified[name] = this.data[name];
9976 this.data[name] = value;
9977 if(!this.editing && this.store){
9978 this.store.afterEdit(this);
9983 * Get the value of the named field.
9984 * @param {String} name The name of the field to get the value of.
9985 * @return {Object} The value of the field.
9987 get : function(name){
9988 return this.data[name];
9992 beginEdit : function(){
9993 this.editing = true;
9998 cancelEdit : function(){
9999 this.editing = false;
10000 delete this.modified;
10004 endEdit : function(){
10005 this.editing = false;
10006 if(this.dirty && this.store){
10007 this.store.afterEdit(this);
10012 * Usually called by the {@link Roo.data.Store} which owns the Record.
10013 * Rejects all changes made to the Record since either creation, or the last commit operation.
10014 * Modified fields are reverted to their original values.
10016 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10017 * of reject operations.
10019 reject : function(){
10020 var m = this.modified;
10022 if(typeof m[n] != "function"){
10023 this.data[n] = m[n];
10026 this.dirty = false;
10027 delete this.modified;
10028 this.editing = false;
10030 this.store.afterReject(this);
10035 * Usually called by the {@link Roo.data.Store} which owns the Record.
10036 * Commits all changes made to the Record since either creation, or the last commit operation.
10038 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10039 * of commit operations.
10041 commit : function(){
10042 this.dirty = false;
10043 delete this.modified;
10044 this.editing = false;
10046 this.store.afterCommit(this);
10051 hasError : function(){
10052 return this.error != null;
10056 clearError : function(){
10061 * Creates a copy of this record.
10062 * @param {String} id (optional) A new record id if you don't want to use this record's id
10065 copy : function(newId) {
10066 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10070 * Ext JS Library 1.1.1
10071 * Copyright(c) 2006-2007, Ext JS, LLC.
10073 * Originally Released Under LGPL - original licence link has changed is not relivant.
10076 * <script type="text/javascript">
10082 * @class Roo.data.Store
10083 * @extends Roo.util.Observable
10084 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10085 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10087 * 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
10088 * has no knowledge of the format of the data returned by the Proxy.<br>
10090 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10091 * instances from the data object. These records are cached and made available through accessor functions.
10093 * Creates a new Store.
10094 * @param {Object} config A config object containing the objects needed for the Store to access data,
10095 * and read the data into Records.
10097 Roo.data.Store = function(config){
10098 this.data = new Roo.util.MixedCollection(false);
10099 this.data.getKey = function(o){
10102 this.baseParams = {};
10104 this.paramNames = {
10109 "multisort" : "_multisort"
10112 if(config && config.data){
10113 this.inlineData = config.data;
10114 delete config.data;
10117 Roo.apply(this, config);
10119 if(this.reader){ // reader passed
10120 this.reader = Roo.factory(this.reader, Roo.data);
10121 this.reader.xmodule = this.xmodule || false;
10122 if(!this.recordType){
10123 this.recordType = this.reader.recordType;
10125 if(this.reader.onMetaChange){
10126 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10130 if(this.recordType){
10131 this.fields = this.recordType.prototype.fields;
10133 this.modified = [];
10137 * @event datachanged
10138 * Fires when the data cache has changed, and a widget which is using this Store
10139 * as a Record cache should refresh its view.
10140 * @param {Store} this
10142 datachanged : true,
10144 * @event metachange
10145 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10146 * @param {Store} this
10147 * @param {Object} meta The JSON metadata
10152 * Fires when Records have been added to the Store
10153 * @param {Store} this
10154 * @param {Roo.data.Record[]} records The array of Records added
10155 * @param {Number} index The index at which the record(s) were added
10160 * Fires when a Record has been removed from the Store
10161 * @param {Store} this
10162 * @param {Roo.data.Record} record The Record that was removed
10163 * @param {Number} index The index at which the record was removed
10168 * Fires when a Record has been updated
10169 * @param {Store} this
10170 * @param {Roo.data.Record} record The Record that was updated
10171 * @param {String} operation The update operation being performed. Value may be one of:
10173 Roo.data.Record.EDIT
10174 Roo.data.Record.REJECT
10175 Roo.data.Record.COMMIT
10181 * Fires when the data cache has been cleared.
10182 * @param {Store} this
10186 * @event beforeload
10187 * Fires before a request is made for a new data object. If the beforeload handler returns false
10188 * the load action will be canceled.
10189 * @param {Store} this
10190 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10194 * @event beforeloadadd
10195 * Fires after a new set of Records has been loaded.
10196 * @param {Store} this
10197 * @param {Roo.data.Record[]} records The Records that were loaded
10198 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10200 beforeloadadd : true,
10203 * Fires after a new set of Records has been loaded, before they are added to the store.
10204 * @param {Store} this
10205 * @param {Roo.data.Record[]} records The Records that were loaded
10206 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10207 * @params {Object} return from reader
10211 * @event loadexception
10212 * Fires if an exception occurs in the Proxy during loading.
10213 * Called with the signature of the Proxy's "loadexception" event.
10214 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10217 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10218 * @param {Object} load options
10219 * @param {Object} jsonData from your request (normally this contains the Exception)
10221 loadexception : true
10225 this.proxy = Roo.factory(this.proxy, Roo.data);
10226 this.proxy.xmodule = this.xmodule || false;
10227 this.relayEvents(this.proxy, ["loadexception"]);
10229 this.sortToggle = {};
10230 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10232 Roo.data.Store.superclass.constructor.call(this);
10234 if(this.inlineData){
10235 this.loadData(this.inlineData);
10236 delete this.inlineData;
10240 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10242 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10243 * without a remote query - used by combo/forms at present.
10247 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10250 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10253 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10254 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10257 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10258 * on any HTTP request
10261 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10264 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10268 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10269 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10271 remoteSort : false,
10274 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10275 * loaded or when a record is removed. (defaults to false).
10277 pruneModifiedRecords : false,
10280 lastOptions : null,
10283 * Add Records to the Store and fires the add event.
10284 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10286 add : function(records){
10287 records = [].concat(records);
10288 for(var i = 0, len = records.length; i < len; i++){
10289 records[i].join(this);
10291 var index = this.data.length;
10292 this.data.addAll(records);
10293 this.fireEvent("add", this, records, index);
10297 * Remove a Record from the Store and fires the remove event.
10298 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10300 remove : function(record){
10301 var index = this.data.indexOf(record);
10302 this.data.removeAt(index);
10303 if(this.pruneModifiedRecords){
10304 this.modified.remove(record);
10306 this.fireEvent("remove", this, record, index);
10310 * Remove all Records from the Store and fires the clear event.
10312 removeAll : function(){
10314 if(this.pruneModifiedRecords){
10315 this.modified = [];
10317 this.fireEvent("clear", this);
10321 * Inserts Records to the Store at the given index and fires the add event.
10322 * @param {Number} index The start index at which to insert the passed Records.
10323 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10325 insert : function(index, records){
10326 records = [].concat(records);
10327 for(var i = 0, len = records.length; i < len; i++){
10328 this.data.insert(index, records[i]);
10329 records[i].join(this);
10331 this.fireEvent("add", this, records, index);
10335 * Get the index within the cache of the passed Record.
10336 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10337 * @return {Number} The index of the passed Record. Returns -1 if not found.
10339 indexOf : function(record){
10340 return this.data.indexOf(record);
10344 * Get the index within the cache of the Record with the passed id.
10345 * @param {String} id The id of the Record to find.
10346 * @return {Number} The index of the Record. Returns -1 if not found.
10348 indexOfId : function(id){
10349 return this.data.indexOfKey(id);
10353 * Get the Record with the specified id.
10354 * @param {String} id The id of the Record to find.
10355 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10357 getById : function(id){
10358 return this.data.key(id);
10362 * Get the Record at the specified index.
10363 * @param {Number} index The index of the Record to find.
10364 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10366 getAt : function(index){
10367 return this.data.itemAt(index);
10371 * Returns a range of Records between specified indices.
10372 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10373 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10374 * @return {Roo.data.Record[]} An array of Records
10376 getRange : function(start, end){
10377 return this.data.getRange(start, end);
10381 storeOptions : function(o){
10382 o = Roo.apply({}, o);
10385 this.lastOptions = o;
10389 * Loads the Record cache from the configured Proxy using the configured Reader.
10391 * If using remote paging, then the first load call must specify the <em>start</em>
10392 * and <em>limit</em> properties in the options.params property to establish the initial
10393 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10395 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10396 * and this call will return before the new data has been loaded. Perform any post-processing
10397 * in a callback function, or in a "load" event handler.</strong>
10399 * @param {Object} options An object containing properties which control loading options:<ul>
10400 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10401 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10402 * passed the following arguments:<ul>
10403 * <li>r : Roo.data.Record[]</li>
10404 * <li>options: Options object from the load call</li>
10405 * <li>success: Boolean success indicator</li></ul></li>
10406 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10407 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10410 load : function(options){
10411 options = options || {};
10412 if(this.fireEvent("beforeload", this, options) !== false){
10413 this.storeOptions(options);
10414 var p = Roo.apply(options.params || {}, this.baseParams);
10415 // if meta was not loaded from remote source.. try requesting it.
10416 if (!this.reader.metaFromRemote) {
10417 p._requestMeta = 1;
10419 if(this.sortInfo && this.remoteSort){
10420 var pn = this.paramNames;
10421 p[pn["sort"]] = this.sortInfo.field;
10422 p[pn["dir"]] = this.sortInfo.direction;
10424 if (this.multiSort) {
10425 var pn = this.paramNames;
10426 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10429 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10434 * Reloads the Record cache from the configured Proxy using the configured Reader and
10435 * the options from the last load operation performed.
10436 * @param {Object} options (optional) An object containing properties which may override the options
10437 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10438 * the most recently used options are reused).
10440 reload : function(options){
10441 this.load(Roo.applyIf(options||{}, this.lastOptions));
10445 // Called as a callback by the Reader during a load operation.
10446 loadRecords : function(o, options, success){
10447 if(!o || success === false){
10448 if(success !== false){
10449 this.fireEvent("load", this, [], options, o);
10451 if(options.callback){
10452 options.callback.call(options.scope || this, [], options, false);
10456 // if data returned failure - throw an exception.
10457 if (o.success === false) {
10458 // show a message if no listener is registered.
10459 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10460 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10462 // loadmask wil be hooked into this..
10463 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10466 var r = o.records, t = o.totalRecords || r.length;
10468 this.fireEvent("beforeloadadd", this, r, options, o);
10470 if(!options || options.add !== true){
10471 if(this.pruneModifiedRecords){
10472 this.modified = [];
10474 for(var i = 0, len = r.length; i < len; i++){
10478 this.data = this.snapshot;
10479 delete this.snapshot;
10482 this.data.addAll(r);
10483 this.totalLength = t;
10485 this.fireEvent("datachanged", this);
10487 this.totalLength = Math.max(t, this.data.length+r.length);
10490 this.fireEvent("load", this, r, options, o);
10491 if(options.callback){
10492 options.callback.call(options.scope || this, r, options, true);
10498 * Loads data from a passed data block. A Reader which understands the format of the data
10499 * must have been configured in the constructor.
10500 * @param {Object} data The data block from which to read the Records. The format of the data expected
10501 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10502 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10504 loadData : function(o, append){
10505 var r = this.reader.readRecords(o);
10506 this.loadRecords(r, {add: append}, true);
10510 * Gets the number of cached records.
10512 * <em>If using paging, this may not be the total size of the dataset. If the data object
10513 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10514 * the data set size</em>
10516 getCount : function(){
10517 return this.data.length || 0;
10521 * Gets the total number of records in the dataset as returned by the server.
10523 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10524 * the dataset size</em>
10526 getTotalCount : function(){
10527 return this.totalLength || 0;
10531 * Returns the sort state of the Store as an object with two properties:
10533 field {String} The name of the field by which the Records are sorted
10534 direction {String} The sort order, "ASC" or "DESC"
10537 getSortState : function(){
10538 return this.sortInfo;
10542 applySort : function(){
10543 if(this.sortInfo && !this.remoteSort){
10544 var s = this.sortInfo, f = s.field;
10545 var st = this.fields.get(f).sortType;
10546 var fn = function(r1, r2){
10547 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10548 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10550 this.data.sort(s.direction, fn);
10551 if(this.snapshot && this.snapshot != this.data){
10552 this.snapshot.sort(s.direction, fn);
10558 * Sets the default sort column and order to be used by the next load operation.
10559 * @param {String} fieldName The name of the field to sort by.
10560 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10562 setDefaultSort : function(field, dir){
10563 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10567 * Sort the Records.
10568 * If remote sorting is used, the sort is performed on the server, and the cache is
10569 * reloaded. If local sorting is used, the cache is sorted internally.
10570 * @param {String} fieldName The name of the field to sort by.
10571 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10573 sort : function(fieldName, dir){
10574 var f = this.fields.get(fieldName);
10576 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10578 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10579 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10584 this.sortToggle[f.name] = dir;
10585 this.sortInfo = {field: f.name, direction: dir};
10586 if(!this.remoteSort){
10588 this.fireEvent("datachanged", this);
10590 this.load(this.lastOptions);
10595 * Calls the specified function for each of the Records in the cache.
10596 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10597 * Returning <em>false</em> aborts and exits the iteration.
10598 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10600 each : function(fn, scope){
10601 this.data.each(fn, scope);
10605 * Gets all records modified since the last commit. Modified records are persisted across load operations
10606 * (e.g., during paging).
10607 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10609 getModifiedRecords : function(){
10610 return this.modified;
10614 createFilterFn : function(property, value, anyMatch){
10615 if(!value.exec){ // not a regex
10616 value = String(value);
10617 if(value.length == 0){
10620 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10622 return function(r){
10623 return value.test(r.data[property]);
10628 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10629 * @param {String} property A field on your records
10630 * @param {Number} start The record index to start at (defaults to 0)
10631 * @param {Number} end The last record index to include (defaults to length - 1)
10632 * @return {Number} The sum
10634 sum : function(property, start, end){
10635 var rs = this.data.items, v = 0;
10636 start = start || 0;
10637 end = (end || end === 0) ? end : rs.length-1;
10639 for(var i = start; i <= end; i++){
10640 v += (rs[i].data[property] || 0);
10646 * Filter the records by a specified property.
10647 * @param {String} field A field on your records
10648 * @param {String/RegExp} value Either a string that the field
10649 * should start with or a RegExp to test against the field
10650 * @param {Boolean} anyMatch True to match any part not just the beginning
10652 filter : function(property, value, anyMatch){
10653 var fn = this.createFilterFn(property, value, anyMatch);
10654 return fn ? this.filterBy(fn) : this.clearFilter();
10658 * Filter by a function. The specified function will be called with each
10659 * record in this data source. If the function returns true the record is included,
10660 * otherwise it is filtered.
10661 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10662 * @param {Object} scope (optional) The scope of the function (defaults to this)
10664 filterBy : function(fn, scope){
10665 this.snapshot = this.snapshot || this.data;
10666 this.data = this.queryBy(fn, scope||this);
10667 this.fireEvent("datachanged", this);
10671 * Query the records by a specified property.
10672 * @param {String} field A field on your records
10673 * @param {String/RegExp} value Either a string that the field
10674 * should start with or a RegExp to test against the field
10675 * @param {Boolean} anyMatch True to match any part not just the beginning
10676 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10678 query : function(property, value, anyMatch){
10679 var fn = this.createFilterFn(property, value, anyMatch);
10680 return fn ? this.queryBy(fn) : this.data.clone();
10684 * Query by a function. The specified function will be called with each
10685 * record in this data source. If the function returns true the record is included
10687 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10688 * @param {Object} scope (optional) The scope of the function (defaults to this)
10689 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10691 queryBy : function(fn, scope){
10692 var data = this.snapshot || this.data;
10693 return data.filterBy(fn, scope||this);
10697 * Collects unique values for a particular dataIndex from this store.
10698 * @param {String} dataIndex The property to collect
10699 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10700 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10701 * @return {Array} An array of the unique values
10703 collect : function(dataIndex, allowNull, bypassFilter){
10704 var d = (bypassFilter === true && this.snapshot) ?
10705 this.snapshot.items : this.data.items;
10706 var v, sv, r = [], l = {};
10707 for(var i = 0, len = d.length; i < len; i++){
10708 v = d[i].data[dataIndex];
10710 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10719 * Revert to a view of the Record cache with no filtering applied.
10720 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10722 clearFilter : function(suppressEvent){
10723 if(this.snapshot && this.snapshot != this.data){
10724 this.data = this.snapshot;
10725 delete this.snapshot;
10726 if(suppressEvent !== true){
10727 this.fireEvent("datachanged", this);
10733 afterEdit : function(record){
10734 if(this.modified.indexOf(record) == -1){
10735 this.modified.push(record);
10737 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10741 afterReject : function(record){
10742 this.modified.remove(record);
10743 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10747 afterCommit : function(record){
10748 this.modified.remove(record);
10749 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10753 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10754 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10756 commitChanges : function(){
10757 var m = this.modified.slice(0);
10758 this.modified = [];
10759 for(var i = 0, len = m.length; i < len; i++){
10765 * Cancel outstanding changes on all changed records.
10767 rejectChanges : function(){
10768 var m = this.modified.slice(0);
10769 this.modified = [];
10770 for(var i = 0, len = m.length; i < len; i++){
10775 onMetaChange : function(meta, rtype, o){
10776 this.recordType = rtype;
10777 this.fields = rtype.prototype.fields;
10778 delete this.snapshot;
10779 this.sortInfo = meta.sortInfo || this.sortInfo;
10780 this.modified = [];
10781 this.fireEvent('metachange', this, this.reader.meta);
10784 moveIndex : function(data, type)
10786 var index = this.indexOf(data);
10788 var newIndex = index + type;
10792 this.insert(newIndex, data);
10797 * Ext JS Library 1.1.1
10798 * Copyright(c) 2006-2007, Ext JS, LLC.
10800 * Originally Released Under LGPL - original licence link has changed is not relivant.
10803 * <script type="text/javascript">
10807 * @class Roo.data.SimpleStore
10808 * @extends Roo.data.Store
10809 * Small helper class to make creating Stores from Array data easier.
10810 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10811 * @cfg {Array} fields An array of field definition objects, or field name strings.
10812 * @cfg {Array} data The multi-dimensional array of data
10814 * @param {Object} config
10816 Roo.data.SimpleStore = function(config){
10817 Roo.data.SimpleStore.superclass.constructor.call(this, {
10819 reader: new Roo.data.ArrayReader({
10822 Roo.data.Record.create(config.fields)
10824 proxy : new Roo.data.MemoryProxy(config.data)
10828 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10830 * Ext JS Library 1.1.1
10831 * Copyright(c) 2006-2007, Ext JS, LLC.
10833 * Originally Released Under LGPL - original licence link has changed is not relivant.
10836 * <script type="text/javascript">
10841 * @extends Roo.data.Store
10842 * @class Roo.data.JsonStore
10843 * Small helper class to make creating Stores for JSON data easier. <br/>
10845 var store = new Roo.data.JsonStore({
10846 url: 'get-images.php',
10848 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10851 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10852 * JsonReader and HttpProxy (unless inline data is provided).</b>
10853 * @cfg {Array} fields An array of field definition objects, or field name strings.
10855 * @param {Object} config
10857 Roo.data.JsonStore = function(c){
10858 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10859 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10860 reader: new Roo.data.JsonReader(c, c.fields)
10863 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10865 * Ext JS Library 1.1.1
10866 * Copyright(c) 2006-2007, Ext JS, LLC.
10868 * Originally Released Under LGPL - original licence link has changed is not relivant.
10871 * <script type="text/javascript">
10875 Roo.data.Field = function(config){
10876 if(typeof config == "string"){
10877 config = {name: config};
10879 Roo.apply(this, config);
10882 this.type = "auto";
10885 var st = Roo.data.SortTypes;
10886 // named sortTypes are supported, here we look them up
10887 if(typeof this.sortType == "string"){
10888 this.sortType = st[this.sortType];
10891 // set default sortType for strings and dates
10892 if(!this.sortType){
10895 this.sortType = st.asUCString;
10898 this.sortType = st.asDate;
10901 this.sortType = st.none;
10906 var stripRe = /[\$,%]/g;
10908 // prebuilt conversion function for this field, instead of
10909 // switching every time we're reading a value
10911 var cv, dateFormat = this.dateFormat;
10916 cv = function(v){ return v; };
10919 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10923 return v !== undefined && v !== null && v !== '' ?
10924 parseInt(String(v).replace(stripRe, ""), 10) : '';
10929 return v !== undefined && v !== null && v !== '' ?
10930 parseFloat(String(v).replace(stripRe, ""), 10) : '';
10935 cv = function(v){ return v === true || v === "true" || v == 1; };
10942 if(v instanceof Date){
10946 if(dateFormat == "timestamp"){
10947 return new Date(v*1000);
10949 return Date.parseDate(v, dateFormat);
10951 var parsed = Date.parse(v);
10952 return parsed ? new Date(parsed) : null;
10961 Roo.data.Field.prototype = {
10969 * Ext JS Library 1.1.1
10970 * Copyright(c) 2006-2007, Ext JS, LLC.
10972 * Originally Released Under LGPL - original licence link has changed is not relivant.
10975 * <script type="text/javascript">
10978 // Base class for reading structured data from a data source. This class is intended to be
10979 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10982 * @class Roo.data.DataReader
10983 * Base class for reading structured data from a data source. This class is intended to be
10984 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10987 Roo.data.DataReader = function(meta, recordType){
10991 this.recordType = recordType instanceof Array ?
10992 Roo.data.Record.create(recordType) : recordType;
10995 Roo.data.DataReader.prototype = {
10997 * Create an empty record
10998 * @param {Object} data (optional) - overlay some values
10999 * @return {Roo.data.Record} record created.
11001 newRow : function(d) {
11003 this.recordType.prototype.fields.each(function(c) {
11005 case 'int' : da[c.name] = 0; break;
11006 case 'date' : da[c.name] = new Date(); break;
11007 case 'float' : da[c.name] = 0.0; break;
11008 case 'boolean' : da[c.name] = false; break;
11009 default : da[c.name] = ""; break;
11013 return new this.recordType(Roo.apply(da, d));
11018 * Ext JS Library 1.1.1
11019 * Copyright(c) 2006-2007, Ext JS, LLC.
11021 * Originally Released Under LGPL - original licence link has changed is not relivant.
11024 * <script type="text/javascript">
11028 * @class Roo.data.DataProxy
11029 * @extends Roo.data.Observable
11030 * This class is an abstract base class for implementations which provide retrieval of
11031 * unformatted data objects.<br>
11033 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11034 * (of the appropriate type which knows how to parse the data object) to provide a block of
11035 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11037 * Custom implementations must implement the load method as described in
11038 * {@link Roo.data.HttpProxy#load}.
11040 Roo.data.DataProxy = function(){
11043 * @event beforeload
11044 * Fires before a network request is made to retrieve a data object.
11045 * @param {Object} This DataProxy object.
11046 * @param {Object} params The params parameter to the load function.
11051 * Fires before the load method's callback is called.
11052 * @param {Object} This DataProxy object.
11053 * @param {Object} o The data object.
11054 * @param {Object} arg The callback argument object passed to the load function.
11058 * @event loadexception
11059 * Fires if an Exception occurs during data retrieval.
11060 * @param {Object} This DataProxy object.
11061 * @param {Object} o The data object.
11062 * @param {Object} arg The callback argument object passed to the load function.
11063 * @param {Object} e The Exception.
11065 loadexception : true
11067 Roo.data.DataProxy.superclass.constructor.call(this);
11070 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11073 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11077 * Ext JS Library 1.1.1
11078 * Copyright(c) 2006-2007, Ext JS, LLC.
11080 * Originally Released Under LGPL - original licence link has changed is not relivant.
11083 * <script type="text/javascript">
11086 * @class Roo.data.MemoryProxy
11087 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11088 * to the Reader when its load method is called.
11090 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11092 Roo.data.MemoryProxy = function(data){
11096 Roo.data.MemoryProxy.superclass.constructor.call(this);
11100 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11103 * Load data from the requested source (in this case an in-memory
11104 * data object passed to the constructor), read the data object into
11105 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11106 * process that block using the passed callback.
11107 * @param {Object} params This parameter is not used by the MemoryProxy class.
11108 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11109 * object into a block of Roo.data.Records.
11110 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11111 * The function must be passed <ul>
11112 * <li>The Record block object</li>
11113 * <li>The "arg" argument from the load function</li>
11114 * <li>A boolean success indicator</li>
11116 * @param {Object} scope The scope in which to call the callback
11117 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11119 load : function(params, reader, callback, scope, arg){
11120 params = params || {};
11123 result = reader.readRecords(this.data);
11125 this.fireEvent("loadexception", this, arg, null, e);
11126 callback.call(scope, null, arg, false);
11129 callback.call(scope, result, arg, true);
11133 update : function(params, records){
11138 * Ext JS Library 1.1.1
11139 * Copyright(c) 2006-2007, Ext JS, LLC.
11141 * Originally Released Under LGPL - original licence link has changed is not relivant.
11144 * <script type="text/javascript">
11147 * @class Roo.data.HttpProxy
11148 * @extends Roo.data.DataProxy
11149 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11150 * configured to reference a certain URL.<br><br>
11152 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11153 * from which the running page was served.<br><br>
11155 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11157 * Be aware that to enable the browser to parse an XML document, the server must set
11158 * the Content-Type header in the HTTP response to "text/xml".
11160 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11161 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11162 * will be used to make the request.
11164 Roo.data.HttpProxy = function(conn){
11165 Roo.data.HttpProxy.superclass.constructor.call(this);
11166 // is conn a conn config or a real conn?
11168 this.useAjax = !conn || !conn.events;
11172 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11173 // thse are take from connection...
11176 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11179 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11180 * extra parameters to each request made by this object. (defaults to undefined)
11183 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11184 * to each request made by this object. (defaults to undefined)
11187 * @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)
11190 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11193 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11199 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11203 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11204 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11205 * a finer-grained basis than the DataProxy events.
11207 getConnection : function(){
11208 return this.useAjax ? Roo.Ajax : this.conn;
11212 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11213 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11214 * process that block using the passed callback.
11215 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11216 * for the request to the remote server.
11217 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11218 * object into a block of Roo.data.Records.
11219 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11220 * The function must be passed <ul>
11221 * <li>The Record block object</li>
11222 * <li>The "arg" argument from the load function</li>
11223 * <li>A boolean success indicator</li>
11225 * @param {Object} scope The scope in which to call the callback
11226 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11228 load : function(params, reader, callback, scope, arg){
11229 if(this.fireEvent("beforeload", this, params) !== false){
11231 params : params || {},
11233 callback : callback,
11238 callback : this.loadResponse,
11242 Roo.applyIf(o, this.conn);
11243 if(this.activeRequest){
11244 Roo.Ajax.abort(this.activeRequest);
11246 this.activeRequest = Roo.Ajax.request(o);
11248 this.conn.request(o);
11251 callback.call(scope||this, null, arg, false);
11256 loadResponse : function(o, success, response){
11257 delete this.activeRequest;
11259 this.fireEvent("loadexception", this, o, response);
11260 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11265 result = o.reader.read(response);
11267 this.fireEvent("loadexception", this, o, response, e);
11268 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11272 this.fireEvent("load", this, o, o.request.arg);
11273 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11277 update : function(dataSet){
11282 updateResponse : function(dataSet){
11287 * Ext JS Library 1.1.1
11288 * Copyright(c) 2006-2007, Ext JS, LLC.
11290 * Originally Released Under LGPL - original licence link has changed is not relivant.
11293 * <script type="text/javascript">
11297 * @class Roo.data.ScriptTagProxy
11298 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11299 * other than the originating domain of the running page.<br><br>
11301 * <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
11302 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11304 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11305 * source code that is used as the source inside a <script> tag.<br><br>
11307 * In order for the browser to process the returned data, the server must wrap the data object
11308 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11309 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11310 * depending on whether the callback name was passed:
11313 boolean scriptTag = false;
11314 String cb = request.getParameter("callback");
11317 response.setContentType("text/javascript");
11319 response.setContentType("application/x-json");
11321 Writer out = response.getWriter();
11323 out.write(cb + "(");
11325 out.print(dataBlock.toJsonString());
11332 * @param {Object} config A configuration object.
11334 Roo.data.ScriptTagProxy = function(config){
11335 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11336 Roo.apply(this, config);
11337 this.head = document.getElementsByTagName("head")[0];
11340 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11342 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11344 * @cfg {String} url The URL from which to request the data object.
11347 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11351 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11352 * the server the name of the callback function set up by the load call to process the returned data object.
11353 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11354 * javascript output which calls this named function passing the data object as its only parameter.
11356 callbackParam : "callback",
11358 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11359 * name to the request.
11364 * Load data from the configured URL, read the data object into
11365 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11366 * process that block using the passed callback.
11367 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11368 * for the request to the remote server.
11369 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11370 * object into a block of Roo.data.Records.
11371 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11372 * The function must be passed <ul>
11373 * <li>The Record block object</li>
11374 * <li>The "arg" argument from the load function</li>
11375 * <li>A boolean success indicator</li>
11377 * @param {Object} scope The scope in which to call the callback
11378 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11380 load : function(params, reader, callback, scope, arg){
11381 if(this.fireEvent("beforeload", this, params) !== false){
11383 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11385 var url = this.url;
11386 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11388 url += "&_dc=" + (new Date().getTime());
11390 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11393 cb : "stcCallback"+transId,
11394 scriptId : "stcScript"+transId,
11398 callback : callback,
11404 window[trans.cb] = function(o){
11405 conn.handleResponse(o, trans);
11408 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11410 if(this.autoAbort !== false){
11414 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11416 var script = document.createElement("script");
11417 script.setAttribute("src", url);
11418 script.setAttribute("type", "text/javascript");
11419 script.setAttribute("id", trans.scriptId);
11420 this.head.appendChild(script);
11422 this.trans = trans;
11424 callback.call(scope||this, null, arg, false);
11429 isLoading : function(){
11430 return this.trans ? true : false;
11434 * Abort the current server request.
11436 abort : function(){
11437 if(this.isLoading()){
11438 this.destroyTrans(this.trans);
11443 destroyTrans : function(trans, isLoaded){
11444 this.head.removeChild(document.getElementById(trans.scriptId));
11445 clearTimeout(trans.timeoutId);
11447 window[trans.cb] = undefined;
11449 delete window[trans.cb];
11452 // if hasn't been loaded, wait for load to remove it to prevent script error
11453 window[trans.cb] = function(){
11454 window[trans.cb] = undefined;
11456 delete window[trans.cb];
11463 handleResponse : function(o, trans){
11464 this.trans = false;
11465 this.destroyTrans(trans, true);
11468 result = trans.reader.readRecords(o);
11470 this.fireEvent("loadexception", this, o, trans.arg, e);
11471 trans.callback.call(trans.scope||window, null, trans.arg, false);
11474 this.fireEvent("load", this, o, trans.arg);
11475 trans.callback.call(trans.scope||window, result, trans.arg, true);
11479 handleFailure : function(trans){
11480 this.trans = false;
11481 this.destroyTrans(trans, false);
11482 this.fireEvent("loadexception", this, null, trans.arg);
11483 trans.callback.call(trans.scope||window, null, trans.arg, false);
11487 * Ext JS Library 1.1.1
11488 * Copyright(c) 2006-2007, Ext JS, LLC.
11490 * Originally Released Under LGPL - original licence link has changed is not relivant.
11493 * <script type="text/javascript">
11497 * @class Roo.data.JsonReader
11498 * @extends Roo.data.DataReader
11499 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11500 * based on mappings in a provided Roo.data.Record constructor.
11502 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11503 * in the reply previously.
11508 var RecordDef = Roo.data.Record.create([
11509 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11510 {name: 'occupation'} // This field will use "occupation" as the mapping.
11512 var myReader = new Roo.data.JsonReader({
11513 totalProperty: "results", // The property which contains the total dataset size (optional)
11514 root: "rows", // The property which contains an Array of row objects
11515 id: "id" // The property within each row object that provides an ID for the record (optional)
11519 * This would consume a JSON file like this:
11521 { 'results': 2, 'rows': [
11522 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11523 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11526 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11527 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11528 * paged from the remote server.
11529 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11530 * @cfg {String} root name of the property which contains the Array of row objects.
11531 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11532 * @cfg {Array} fields Array of field definition objects
11534 * Create a new JsonReader
11535 * @param {Object} meta Metadata configuration options
11536 * @param {Object} recordType Either an Array of field definition objects,
11537 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11539 Roo.data.JsonReader = function(meta, recordType){
11542 // set some defaults:
11543 Roo.applyIf(meta, {
11544 totalProperty: 'total',
11545 successProperty : 'success',
11550 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11552 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11555 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11556 * Used by Store query builder to append _requestMeta to params.
11559 metaFromRemote : false,
11561 * This method is only used by a DataProxy which has retrieved data from a remote server.
11562 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11563 * @return {Object} data A data block which is used by an Roo.data.Store object as
11564 * a cache of Roo.data.Records.
11566 read : function(response){
11567 var json = response.responseText;
11569 var o = /* eval:var:o */ eval("("+json+")");
11571 throw {message: "JsonReader.read: Json object not found"};
11577 this.metaFromRemote = true;
11578 this.meta = o.metaData;
11579 this.recordType = Roo.data.Record.create(o.metaData.fields);
11580 this.onMetaChange(this.meta, this.recordType, o);
11582 return this.readRecords(o);
11585 // private function a store will implement
11586 onMetaChange : function(meta, recordType, o){
11593 simpleAccess: function(obj, subsc) {
11600 getJsonAccessor: function(){
11602 return function(expr) {
11604 return(re.test(expr))
11605 ? new Function("obj", "return obj." + expr)
11610 return Roo.emptyFn;
11615 * Create a data block containing Roo.data.Records from an XML document.
11616 * @param {Object} o An object which contains an Array of row objects in the property specified
11617 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11618 * which contains the total size of the dataset.
11619 * @return {Object} data A data block which is used by an Roo.data.Store object as
11620 * a cache of Roo.data.Records.
11622 readRecords : function(o){
11624 * After any data loads, the raw JSON data is available for further custom processing.
11628 var s = this.meta, Record = this.recordType,
11629 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11631 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11633 if(s.totalProperty) {
11634 this.getTotal = this.getJsonAccessor(s.totalProperty);
11636 if(s.successProperty) {
11637 this.getSuccess = this.getJsonAccessor(s.successProperty);
11639 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11641 var g = this.getJsonAccessor(s.id);
11642 this.getId = function(rec) {
11644 return (r === undefined || r === "") ? null : r;
11647 this.getId = function(){return null;};
11650 for(var jj = 0; jj < fl; jj++){
11652 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11653 this.ef[jj] = this.getJsonAccessor(map);
11657 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11658 if(s.totalProperty){
11659 var vt = parseInt(this.getTotal(o), 10);
11664 if(s.successProperty){
11665 var vs = this.getSuccess(o);
11666 if(vs === false || vs === 'false'){
11671 for(var i = 0; i < c; i++){
11674 var id = this.getId(n);
11675 for(var j = 0; j < fl; j++){
11677 var v = this.ef[j](n);
11679 Roo.log('missing convert for ' + f.name);
11683 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11685 var record = new Record(values, id);
11687 records[i] = record;
11693 totalRecords : totalRecords
11698 * Ext JS Library 1.1.1
11699 * Copyright(c) 2006-2007, Ext JS, LLC.
11701 * Originally Released Under LGPL - original licence link has changed is not relivant.
11704 * <script type="text/javascript">
11708 * @class Roo.data.ArrayReader
11709 * @extends Roo.data.DataReader
11710 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11711 * Each element of that Array represents a row of data fields. The
11712 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11713 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11717 var RecordDef = Roo.data.Record.create([
11718 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11719 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11721 var myReader = new Roo.data.ArrayReader({
11722 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11726 * This would consume an Array like this:
11728 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11730 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11732 * Create a new JsonReader
11733 * @param {Object} meta Metadata configuration options.
11734 * @param {Object} recordType Either an Array of field definition objects
11735 * as specified to {@link Roo.data.Record#create},
11736 * or an {@link Roo.data.Record} object
11737 * created using {@link Roo.data.Record#create}.
11739 Roo.data.ArrayReader = function(meta, recordType){
11740 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11743 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11745 * Create a data block containing Roo.data.Records from an XML document.
11746 * @param {Object} o An Array of row objects which represents the dataset.
11747 * @return {Object} data A data block which is used by an Roo.data.Store object as
11748 * a cache of Roo.data.Records.
11750 readRecords : function(o){
11751 var sid = this.meta ? this.meta.id : null;
11752 var recordType = this.recordType, fields = recordType.prototype.fields;
11755 for(var i = 0; i < root.length; i++){
11758 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11759 for(var j = 0, jlen = fields.length; j < jlen; j++){
11760 var f = fields.items[j];
11761 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11762 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11764 values[f.name] = v;
11766 var record = new recordType(values, id);
11768 records[records.length] = record;
11772 totalRecords : records.length
11781 * @class Roo.bootstrap.ComboBox
11782 * @extends Roo.bootstrap.TriggerField
11783 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11784 * @cfg {Boolean} append (true|false) default false
11785 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11786 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11787 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11788 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11789 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11790 * @cfg {Boolean} animate default true
11791 * @cfg {Boolean} emptyResultText only for touch device
11792 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11794 * Create a new ComboBox.
11795 * @param {Object} config Configuration options
11797 Roo.bootstrap.ComboBox = function(config){
11798 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11802 * Fires when the dropdown list is expanded
11803 * @param {Roo.bootstrap.ComboBox} combo This combo box
11808 * Fires when the dropdown list is collapsed
11809 * @param {Roo.bootstrap.ComboBox} combo This combo box
11813 * @event beforeselect
11814 * Fires before a list item is selected. Return false to cancel the selection.
11815 * @param {Roo.bootstrap.ComboBox} combo This combo box
11816 * @param {Roo.data.Record} record The data record returned from the underlying store
11817 * @param {Number} index The index of the selected item in the dropdown list
11819 'beforeselect' : true,
11822 * Fires when a list item is selected
11823 * @param {Roo.bootstrap.ComboBox} combo This combo box
11824 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11825 * @param {Number} index The index of the selected item in the dropdown list
11829 * @event beforequery
11830 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11831 * The event object passed has these properties:
11832 * @param {Roo.bootstrap.ComboBox} combo This combo box
11833 * @param {String} query The query
11834 * @param {Boolean} forceAll true to force "all" query
11835 * @param {Boolean} cancel true to cancel the query
11836 * @param {Object} e The query event object
11838 'beforequery': true,
11841 * Fires when the 'add' icon is pressed (add a listener to enable add button)
11842 * @param {Roo.bootstrap.ComboBox} combo This combo box
11847 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11848 * @param {Roo.bootstrap.ComboBox} combo This combo box
11849 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11854 * Fires when the remove value from the combobox array
11855 * @param {Roo.bootstrap.ComboBox} combo This combo box
11859 * @event afterremove
11860 * Fires when the remove value from the combobox array
11861 * @param {Roo.bootstrap.ComboBox} combo This combo box
11863 'afterremove' : true,
11865 * @event specialfilter
11866 * Fires when specialfilter
11867 * @param {Roo.bootstrap.ComboBox} combo This combo box
11869 'specialfilter' : true,
11872 * Fires when tick the element
11873 * @param {Roo.bootstrap.ComboBox} combo This combo box
11877 * @event touchviewdisplay
11878 * Fires when touch view require special display (default is using displayField)
11879 * @param {Roo.bootstrap.ComboBox} combo This combo box
11880 * @param {Object} cfg set html .
11882 'touchviewdisplay' : true
11887 this.tickItems = [];
11889 this.selectedIndex = -1;
11890 if(this.mode == 'local'){
11891 if(config.queryDelay === undefined){
11892 this.queryDelay = 10;
11894 if(config.minChars === undefined){
11900 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11903 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11904 * rendering into an Roo.Editor, defaults to false)
11907 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11908 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11911 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11914 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11915 * the dropdown list (defaults to undefined, with no header element)
11919 * @cfg {String/Roo.Template} tpl The template to use to render the output
11923 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11925 listWidth: undefined,
11927 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11928 * mode = 'remote' or 'text' if mode = 'local')
11930 displayField: undefined,
11933 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11934 * mode = 'remote' or 'value' if mode = 'local').
11935 * Note: use of a valueField requires the user make a selection
11936 * in order for a value to be mapped.
11938 valueField: undefined,
11942 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11943 * field's data value (defaults to the underlying DOM element's name)
11945 hiddenName: undefined,
11947 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11951 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11953 selectedClass: 'active',
11956 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11960 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11961 * anchor positions (defaults to 'tl-bl')
11963 listAlign: 'tl-bl?',
11965 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11969 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
11970 * query specified by the allQuery config option (defaults to 'query')
11972 triggerAction: 'query',
11974 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11975 * (defaults to 4, does not apply if editable = false)
11979 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11980 * delay (typeAheadDelay) if it matches a known value (defaults to false)
11984 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11985 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11989 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11990 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
11994 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
11995 * when editable = true (defaults to false)
11997 selectOnFocus:false,
11999 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12001 queryParam: 'query',
12003 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12004 * when mode = 'remote' (defaults to 'Loading...')
12006 loadingText: 'Loading...',
12008 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12012 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12016 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12017 * traditional select (defaults to true)
12021 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12025 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12029 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12030 * listWidth has a higher value)
12034 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12035 * allow the user to set arbitrary text into the field (defaults to false)
12037 forceSelection:false,
12039 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12040 * if typeAhead = true (defaults to 250)
12042 typeAheadDelay : 250,
12044 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12045 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12047 valueNotFoundText : undefined,
12049 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12051 blockFocus : false,
12054 * @cfg {Boolean} disableClear Disable showing of clear button.
12056 disableClear : false,
12058 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12060 alwaysQuery : false,
12063 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12068 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12070 invalidClass : "has-warning",
12073 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12075 validClass : "has-success",
12078 * @cfg {Boolean} specialFilter (true|false) special filter default false
12080 specialFilter : false,
12083 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12085 mobileTouchView : true,
12097 btnPosition : 'right',
12098 triggerList : true,
12099 showToggleBtn : true,
12101 emptyResultText: 'Empty',
12102 triggerText : 'Select',
12104 // element that contains real text value.. (when hidden is used..)
12106 getAutoCreate : function()
12114 if(Roo.isTouch && this.mobileTouchView){
12115 cfg = this.getAutoCreateTouchView();
12122 if(!this.tickable){
12123 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12128 * ComboBox with tickable selections
12131 var align = this.labelAlign || this.parentLabelAlign();
12134 cls : 'form-group roo-combobox-tickable' //input-group
12139 cls : 'tickable-buttons',
12144 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12145 html : this.triggerText
12151 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12158 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12165 buttons.cn.unshift({
12167 cls: 'roo-select2-search-field-input'
12173 Roo.each(buttons.cn, function(c){
12175 c.cls += ' btn-' + _this.size;
12178 if (_this.disabled) {
12189 cls: 'form-hidden-field'
12193 cls: 'roo-select2-choices',
12197 cls: 'roo-select2-search-field',
12209 cls: 'roo-select2-container input-group roo-select2-container-multi',
12214 // cls: 'typeahead typeahead-long dropdown-menu',
12215 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12220 if(this.hasFeedback && !this.allowBlank){
12224 cls: 'glyphicon form-control-feedback'
12227 combobox.cn.push(feedback);
12230 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12232 // Roo.log("left and has label");
12238 cls : 'control-label col-sm-' + this.labelWidth,
12239 html : this.fieldLabel
12243 cls : "col-sm-" + (12 - this.labelWidth),
12250 } else if ( this.fieldLabel.length) {
12251 // Roo.log(" label");
12256 //cls : 'input-group-addon',
12257 html : this.fieldLabel
12267 // Roo.log(" no label && no align");
12274 ['xs','sm','md','lg'].map(function(size){
12275 if (settings[size]) {
12276 cfg.cls += ' col-' + size + '-' + settings[size];
12284 _initEventsCalled : false,
12287 initEvents: function()
12290 if (this._initEventsCalled) { // as we call render... prevent looping...
12293 this._initEventsCalled = true;
12296 throw "can not find store for combo";
12299 this.store = Roo.factory(this.store, Roo.data);
12301 // if we are building from html. then this element is so complex, that we can not really
12302 // use the rendered HTML.
12303 // so we have to trash and replace the previous code.
12304 if (Roo.XComponent.build_from_html) {
12306 // remove this element....
12307 var e = this.el.dom, k=0;
12308 while (e ) { e = e.previousSibling; ++k;}
12313 this.rendered = false;
12315 this.render(this.parent().getChildContainer(true), k);
12326 if(Roo.isTouch && this.mobileTouchView){
12327 this.initTouchView();
12332 this.initTickableEvents();
12336 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12338 if(this.hiddenName){
12340 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12342 this.hiddenField.dom.value =
12343 this.hiddenValue !== undefined ? this.hiddenValue :
12344 this.value !== undefined ? this.value : '';
12346 // prevent input submission
12347 this.el.dom.removeAttribute('name');
12348 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12353 // this.el.dom.setAttribute('autocomplete', 'off');
12356 var cls = 'x-combo-list';
12358 //this.list = new Roo.Layer({
12359 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12365 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12366 _this.list.setWidth(lw);
12369 this.list.on('mouseover', this.onViewOver, this);
12370 this.list.on('mousemove', this.onViewMove, this);
12372 this.list.on('scroll', this.onViewScroll, this);
12375 this.list.swallowEvent('mousewheel');
12376 this.assetHeight = 0;
12379 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12380 this.assetHeight += this.header.getHeight();
12383 this.innerList = this.list.createChild({cls:cls+'-inner'});
12384 this.innerList.on('mouseover', this.onViewOver, this);
12385 this.innerList.on('mousemove', this.onViewMove, this);
12386 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12388 if(this.allowBlank && !this.pageSize && !this.disableClear){
12389 this.footer = this.list.createChild({cls:cls+'-ft'});
12390 this.pageTb = new Roo.Toolbar(this.footer);
12394 this.footer = this.list.createChild({cls:cls+'-ft'});
12395 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12396 {pageSize: this.pageSize});
12400 if (this.pageTb && this.allowBlank && !this.disableClear) {
12402 this.pageTb.add(new Roo.Toolbar.Fill(), {
12403 cls: 'x-btn-icon x-btn-clear',
12405 handler: function()
12408 _this.clearValue();
12409 _this.onSelect(false, -1);
12414 this.assetHeight += this.footer.getHeight();
12419 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12422 this.view = new Roo.View(this.list, this.tpl, {
12423 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12425 //this.view.wrapEl.setDisplayed(false);
12426 this.view.on('click', this.onViewClick, this);
12430 this.store.on('beforeload', this.onBeforeLoad, this);
12431 this.store.on('load', this.onLoad, this);
12432 this.store.on('loadexception', this.onLoadException, this);
12434 if(this.resizable){
12435 this.resizer = new Roo.Resizable(this.list, {
12436 pinned:true, handles:'se'
12438 this.resizer.on('resize', function(r, w, h){
12439 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12440 this.listWidth = w;
12441 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12442 this.restrictHeight();
12444 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12447 if(!this.editable){
12448 this.editable = true;
12449 this.setEditable(false);
12454 if (typeof(this.events.add.listeners) != 'undefined') {
12456 this.addicon = this.wrap.createChild(
12457 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12459 this.addicon.on('click', function(e) {
12460 this.fireEvent('add', this);
12463 if (typeof(this.events.edit.listeners) != 'undefined') {
12465 this.editicon = this.wrap.createChild(
12466 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12467 if (this.addicon) {
12468 this.editicon.setStyle('margin-left', '40px');
12470 this.editicon.on('click', function(e) {
12472 // we fire even if inothing is selected..
12473 this.fireEvent('edit', this, this.lastData );
12479 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12480 "up" : function(e){
12481 this.inKeyMode = true;
12485 "down" : function(e){
12486 if(!this.isExpanded()){
12487 this.onTriggerClick();
12489 this.inKeyMode = true;
12494 "enter" : function(e){
12495 // this.onViewClick();
12499 if(this.fireEvent("specialkey", this, e)){
12500 this.onViewClick(false);
12506 "esc" : function(e){
12510 "tab" : function(e){
12513 if(this.fireEvent("specialkey", this, e)){
12514 this.onViewClick(false);
12522 doRelay : function(foo, bar, hname){
12523 if(hname == 'down' || this.scope.isExpanded()){
12524 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12533 this.queryDelay = Math.max(this.queryDelay || 10,
12534 this.mode == 'local' ? 10 : 250);
12537 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12539 if(this.typeAhead){
12540 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12542 if(this.editable !== false){
12543 this.inputEl().on("keyup", this.onKeyUp, this);
12545 if(this.forceSelection){
12546 this.inputEl().on('blur', this.doForce, this);
12550 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12551 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12555 initTickableEvents: function()
12559 if(this.hiddenName){
12561 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12563 this.hiddenField.dom.value =
12564 this.hiddenValue !== undefined ? this.hiddenValue :
12565 this.value !== undefined ? this.value : '';
12567 // prevent input submission
12568 this.el.dom.removeAttribute('name');
12569 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12574 // this.list = this.el.select('ul.dropdown-menu',true).first();
12576 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12577 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12578 if(this.triggerList){
12579 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12582 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12583 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12585 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12586 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12588 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12589 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12591 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12592 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12593 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12596 this.cancelBtn.hide();
12601 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12602 _this.list.setWidth(lw);
12605 this.list.on('mouseover', this.onViewOver, this);
12606 this.list.on('mousemove', this.onViewMove, this);
12608 this.list.on('scroll', this.onViewScroll, this);
12611 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>';
12614 this.view = new Roo.View(this.list, this.tpl, {
12615 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12618 //this.view.wrapEl.setDisplayed(false);
12619 this.view.on('click', this.onViewClick, this);
12623 this.store.on('beforeload', this.onBeforeLoad, this);
12624 this.store.on('load', this.onLoad, this);
12625 this.store.on('loadexception', this.onLoadException, this);
12628 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12629 "up" : function(e){
12630 this.inKeyMode = true;
12634 "down" : function(e){
12635 this.inKeyMode = true;
12639 "enter" : function(e){
12640 if(this.fireEvent("specialkey", this, e)){
12641 this.onViewClick(false);
12647 "esc" : function(e){
12648 this.onTickableFooterButtonClick(e, false, false);
12651 "tab" : function(e){
12652 this.fireEvent("specialkey", this, e);
12654 this.onTickableFooterButtonClick(e, false, false);
12661 doRelay : function(e, fn, key){
12662 if(this.scope.isExpanded()){
12663 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12672 this.queryDelay = Math.max(this.queryDelay || 10,
12673 this.mode == 'local' ? 10 : 250);
12676 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12678 if(this.typeAhead){
12679 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12682 if(this.editable !== false){
12683 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12688 onDestroy : function(){
12690 this.view.setStore(null);
12691 this.view.el.removeAllListeners();
12692 this.view.el.remove();
12693 this.view.purgeListeners();
12696 this.list.dom.innerHTML = '';
12700 this.store.un('beforeload', this.onBeforeLoad, this);
12701 this.store.un('load', this.onLoad, this);
12702 this.store.un('loadexception', this.onLoadException, this);
12704 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12708 fireKey : function(e){
12709 if(e.isNavKeyPress() && !this.list.isVisible()){
12710 this.fireEvent("specialkey", this, e);
12715 onResize: function(w, h){
12716 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12718 // if(typeof w != 'number'){
12719 // // we do not handle it!?!?
12722 // var tw = this.trigger.getWidth();
12723 // // tw += this.addicon ? this.addicon.getWidth() : 0;
12724 // // tw += this.editicon ? this.editicon.getWidth() : 0;
12726 // this.inputEl().setWidth( this.adjustWidth('input', x));
12728 // //this.trigger.setStyle('left', x+'px');
12730 // if(this.list && this.listWidth === undefined){
12731 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12732 // this.list.setWidth(lw);
12733 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12741 * Allow or prevent the user from directly editing the field text. If false is passed,
12742 * the user will only be able to select from the items defined in the dropdown list. This method
12743 * is the runtime equivalent of setting the 'editable' config option at config time.
12744 * @param {Boolean} value True to allow the user to directly edit the field text
12746 setEditable : function(value){
12747 if(value == this.editable){
12750 this.editable = value;
12752 this.inputEl().dom.setAttribute('readOnly', true);
12753 this.inputEl().on('mousedown', this.onTriggerClick, this);
12754 this.inputEl().addClass('x-combo-noedit');
12756 this.inputEl().dom.setAttribute('readOnly', false);
12757 this.inputEl().un('mousedown', this.onTriggerClick, this);
12758 this.inputEl().removeClass('x-combo-noedit');
12764 onBeforeLoad : function(combo,opts){
12765 if(!this.hasFocus){
12769 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12771 this.restrictHeight();
12772 this.selectedIndex = -1;
12776 onLoad : function(){
12778 this.hasQuery = false;
12780 if(!this.hasFocus){
12784 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12785 this.loading.hide();
12788 if(this.store.getCount() > 0){
12790 this.restrictHeight();
12791 if(this.lastQuery == this.allQuery){
12792 if(this.editable && !this.tickable){
12793 this.inputEl().dom.select();
12797 !this.selectByValue(this.value, true) &&
12800 !this.store.lastOptions ||
12801 typeof(this.store.lastOptions.add) == 'undefined' ||
12802 this.store.lastOptions.add != true
12805 this.select(0, true);
12808 if(this.autoFocus){
12811 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12812 this.taTask.delay(this.typeAheadDelay);
12816 this.onEmptyResults();
12822 onLoadException : function()
12824 this.hasQuery = false;
12826 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12827 this.loading.hide();
12830 if(this.tickable && this.editable){
12835 // only causes errors at present
12836 //Roo.log(this.store.reader.jsonData);
12837 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12839 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12845 onTypeAhead : function(){
12846 if(this.store.getCount() > 0){
12847 var r = this.store.getAt(0);
12848 var newValue = r.data[this.displayField];
12849 var len = newValue.length;
12850 var selStart = this.getRawValue().length;
12852 if(selStart != len){
12853 this.setRawValue(newValue);
12854 this.selectText(selStart, newValue.length);
12860 onSelect : function(record, index){
12862 if(this.fireEvent('beforeselect', this, record, index) !== false){
12864 this.setFromData(index > -1 ? record.data : false);
12867 this.fireEvent('select', this, record, index);
12872 * Returns the currently selected field value or empty string if no value is set.
12873 * @return {String} value The selected value
12875 getValue : function(){
12878 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12881 if(this.valueField){
12882 return typeof this.value != 'undefined' ? this.value : '';
12884 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12889 * Clears any text/value currently set in the field
12891 clearValue : function(){
12892 if(this.hiddenField){
12893 this.hiddenField.dom.value = '';
12896 this.setRawValue('');
12897 this.lastSelectionText = '';
12898 this.lastData = false;
12900 var close = this.closeTriggerEl();
12909 * Sets the specified value into the field. If the value finds a match, the corresponding record text
12910 * will be displayed in the field. If the value does not match the data value of an existing item,
12911 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12912 * Otherwise the field will be blank (although the value will still be set).
12913 * @param {String} value The value to match
12915 setValue : function(v){
12922 if(this.valueField){
12923 var r = this.findRecord(this.valueField, v);
12925 text = r.data[this.displayField];
12926 }else if(this.valueNotFoundText !== undefined){
12927 text = this.valueNotFoundText;
12930 this.lastSelectionText = text;
12931 if(this.hiddenField){
12932 this.hiddenField.dom.value = v;
12934 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12937 var close = this.closeTriggerEl();
12940 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12944 * @property {Object} the last set data for the element
12949 * Sets the value of the field based on a object which is related to the record format for the store.
12950 * @param {Object} value the value to set as. or false on reset?
12952 setFromData : function(o){
12959 var dv = ''; // display value
12960 var vv = ''; // value value..
12962 if (this.displayField) {
12963 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12965 // this is an error condition!!!
12966 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
12969 if(this.valueField){
12970 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12973 var close = this.closeTriggerEl();
12976 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12979 if(this.hiddenField){
12980 this.hiddenField.dom.value = vv;
12982 this.lastSelectionText = dv;
12983 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12987 // no hidden field.. - we store the value in 'value', but still display
12988 // display field!!!!
12989 this.lastSelectionText = dv;
12990 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12997 reset : function(){
12998 // overridden so that last data is reset..
13005 this.setValue(this.originalValue);
13006 this.clearInvalid();
13007 this.lastData = false;
13009 this.view.clearSelections();
13013 findRecord : function(prop, value){
13015 if(this.store.getCount() > 0){
13016 this.store.each(function(r){
13017 if(r.data[prop] == value){
13027 getName: function()
13029 // returns hidden if it's set..
13030 if (!this.rendered) {return ''};
13031 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13035 onViewMove : function(e, t){
13036 this.inKeyMode = false;
13040 onViewOver : function(e, t){
13041 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13044 var item = this.view.findItemFromChild(t);
13047 var index = this.view.indexOf(item);
13048 this.select(index, false);
13053 onViewClick : function(view, doFocus, el, e)
13055 var index = this.view.getSelectedIndexes()[0];
13057 var r = this.store.getAt(index);
13061 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13068 Roo.each(this.tickItems, function(v,k){
13070 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13072 _this.tickItems.splice(k, 1);
13074 if(typeof(e) == 'undefined' && view == false){
13075 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13087 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13088 this.tickItems.push(r.data);
13091 if(typeof(e) == 'undefined' && view == false){
13092 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13099 this.onSelect(r, index);
13101 if(doFocus !== false && !this.blockFocus){
13102 this.inputEl().focus();
13107 restrictHeight : function(){
13108 //this.innerList.dom.style.height = '';
13109 //var inner = this.innerList.dom;
13110 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13111 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13112 //this.list.beginUpdate();
13113 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13114 this.list.alignTo(this.inputEl(), this.listAlign);
13115 this.list.alignTo(this.inputEl(), this.listAlign);
13116 //this.list.endUpdate();
13120 onEmptyResults : function(){
13122 if(this.tickable && this.editable){
13123 this.restrictHeight();
13131 * Returns true if the dropdown list is expanded, else false.
13133 isExpanded : function(){
13134 return this.list.isVisible();
13138 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13139 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13140 * @param {String} value The data value of the item to select
13141 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13142 * selected item if it is not currently in view (defaults to true)
13143 * @return {Boolean} True if the value matched an item in the list, else false
13145 selectByValue : function(v, scrollIntoView){
13146 if(v !== undefined && v !== null){
13147 var r = this.findRecord(this.valueField || this.displayField, v);
13149 this.select(this.store.indexOf(r), scrollIntoView);
13157 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13158 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13159 * @param {Number} index The zero-based index of the list item to select
13160 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13161 * selected item if it is not currently in view (defaults to true)
13163 select : function(index, scrollIntoView){
13164 this.selectedIndex = index;
13165 this.view.select(index);
13166 if(scrollIntoView !== false){
13167 var el = this.view.getNode(index);
13169 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13172 this.list.scrollChildIntoView(el, false);
13178 selectNext : function(){
13179 var ct = this.store.getCount();
13181 if(this.selectedIndex == -1){
13183 }else if(this.selectedIndex < ct-1){
13184 this.select(this.selectedIndex+1);
13190 selectPrev : function(){
13191 var ct = this.store.getCount();
13193 if(this.selectedIndex == -1){
13195 }else if(this.selectedIndex != 0){
13196 this.select(this.selectedIndex-1);
13202 onKeyUp : function(e){
13203 if(this.editable !== false && !e.isSpecialKey()){
13204 this.lastKey = e.getKey();
13205 this.dqTask.delay(this.queryDelay);
13210 validateBlur : function(){
13211 return !this.list || !this.list.isVisible();
13215 initQuery : function(){
13217 var v = this.getRawValue();
13219 if(this.tickable && this.editable){
13220 v = this.tickableInputEl().getValue();
13227 doForce : function(){
13228 if(this.inputEl().dom.value.length > 0){
13229 this.inputEl().dom.value =
13230 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13236 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13237 * query allowing the query action to be canceled if needed.
13238 * @param {String} query The SQL query to execute
13239 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13240 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13241 * saved in the current store (defaults to false)
13243 doQuery : function(q, forceAll){
13245 if(q === undefined || q === null){
13250 forceAll: forceAll,
13254 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13259 forceAll = qe.forceAll;
13260 if(forceAll === true || (q.length >= this.minChars)){
13262 this.hasQuery = true;
13264 if(this.lastQuery != q || this.alwaysQuery){
13265 this.lastQuery = q;
13266 if(this.mode == 'local'){
13267 this.selectedIndex = -1;
13269 this.store.clearFilter();
13272 if(this.specialFilter){
13273 this.fireEvent('specialfilter', this);
13278 this.store.filter(this.displayField, q);
13281 this.store.fireEvent("datachanged", this.store);
13288 this.store.baseParams[this.queryParam] = q;
13290 var options = {params : this.getParams(q)};
13293 options.add = true;
13294 options.params.start = this.page * this.pageSize;
13297 this.store.load(options);
13300 * this code will make the page width larger, at the beginning, the list not align correctly,
13301 * we should expand the list on onLoad
13302 * so command out it
13307 this.selectedIndex = -1;
13312 this.loadNext = false;
13316 getParams : function(q){
13318 //p[this.queryParam] = q;
13322 p.limit = this.pageSize;
13328 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13330 collapse : function(){
13331 if(!this.isExpanded()){
13338 this.hasFocus = false;
13340 this.cancelBtn.hide();
13341 this.trigger.show();
13344 this.tickableInputEl().dom.value = '';
13345 this.tickableInputEl().blur();
13350 Roo.get(document).un('mousedown', this.collapseIf, this);
13351 Roo.get(document).un('mousewheel', this.collapseIf, this);
13352 if (!this.editable) {
13353 Roo.get(document).un('keydown', this.listKeyPress, this);
13355 this.fireEvent('collapse', this);
13359 collapseIf : function(e){
13360 var in_combo = e.within(this.el);
13361 var in_list = e.within(this.list);
13362 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13364 if (in_combo || in_list || is_list) {
13365 //e.stopPropagation();
13370 this.onTickableFooterButtonClick(e, false, false);
13378 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13380 expand : function(){
13382 if(this.isExpanded() || !this.hasFocus){
13386 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13387 this.list.setWidth(lw);
13394 this.restrictHeight();
13398 this.tickItems = Roo.apply([], this.item);
13401 this.cancelBtn.show();
13402 this.trigger.hide();
13405 this.tickableInputEl().focus();
13410 Roo.get(document).on('mousedown', this.collapseIf, this);
13411 Roo.get(document).on('mousewheel', this.collapseIf, this);
13412 if (!this.editable) {
13413 Roo.get(document).on('keydown', this.listKeyPress, this);
13416 this.fireEvent('expand', this);
13420 // Implements the default empty TriggerField.onTriggerClick function
13421 onTriggerClick : function(e)
13423 Roo.log('trigger click');
13425 if(this.disabled || !this.triggerList){
13430 this.loadNext = false;
13432 if(this.isExpanded()){
13434 if (!this.blockFocus) {
13435 this.inputEl().focus();
13439 this.hasFocus = true;
13440 if(this.triggerAction == 'all') {
13441 this.doQuery(this.allQuery, true);
13443 this.doQuery(this.getRawValue());
13445 if (!this.blockFocus) {
13446 this.inputEl().focus();
13451 onTickableTriggerClick : function(e)
13458 this.loadNext = false;
13459 this.hasFocus = true;
13461 if(this.triggerAction == 'all') {
13462 this.doQuery(this.allQuery, true);
13464 this.doQuery(this.getRawValue());
13468 onSearchFieldClick : function(e)
13470 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13471 this.onTickableFooterButtonClick(e, false, false);
13475 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13480 this.loadNext = false;
13481 this.hasFocus = true;
13483 if(this.triggerAction == 'all') {
13484 this.doQuery(this.allQuery, true);
13486 this.doQuery(this.getRawValue());
13490 listKeyPress : function(e)
13492 //Roo.log('listkeypress');
13493 // scroll to first matching element based on key pres..
13494 if (e.isSpecialKey()) {
13497 var k = String.fromCharCode(e.getKey()).toUpperCase();
13500 var csel = this.view.getSelectedNodes();
13501 var cselitem = false;
13503 var ix = this.view.indexOf(csel[0]);
13504 cselitem = this.store.getAt(ix);
13505 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13511 this.store.each(function(v) {
13513 // start at existing selection.
13514 if (cselitem.id == v.id) {
13520 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13521 match = this.store.indexOf(v);
13527 if (match === false) {
13528 return true; // no more action?
13531 this.view.select(match);
13532 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13533 sn.scrollIntoView(sn.dom.parentNode, false);
13536 onViewScroll : function(e, t){
13538 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){
13542 this.hasQuery = true;
13544 this.loading = this.list.select('.loading', true).first();
13546 if(this.loading === null){
13547 this.list.createChild({
13549 cls: 'loading roo-select2-more-results roo-select2-active',
13550 html: 'Loading more results...'
13553 this.loading = this.list.select('.loading', true).first();
13555 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13557 this.loading.hide();
13560 this.loading.show();
13565 this.loadNext = true;
13567 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13572 addItem : function(o)
13574 var dv = ''; // display value
13576 if (this.displayField) {
13577 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13579 // this is an error condition!!!
13580 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13587 var choice = this.choices.createChild({
13589 cls: 'roo-select2-search-choice',
13598 cls: 'roo-select2-search-choice-close',
13603 }, this.searchField);
13605 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13607 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13615 this.inputEl().dom.value = '';
13620 onRemoveItem : function(e, _self, o)
13622 e.preventDefault();
13624 this.lastItem = Roo.apply([], this.item);
13626 var index = this.item.indexOf(o.data) * 1;
13629 Roo.log('not this item?!');
13633 this.item.splice(index, 1);
13638 this.fireEvent('remove', this, e);
13644 syncValue : function()
13646 if(!this.item.length){
13653 Roo.each(this.item, function(i){
13654 if(_this.valueField){
13655 value.push(i[_this.valueField]);
13662 this.value = value.join(',');
13664 if(this.hiddenField){
13665 this.hiddenField.dom.value = this.value;
13668 this.store.fireEvent("datachanged", this.store);
13671 clearItem : function()
13673 if(!this.multiple){
13679 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13687 if(this.tickable && !Roo.isTouch){
13688 this.view.refresh();
13692 inputEl: function ()
13694 if(Roo.isTouch && this.mobileTouchView){
13695 return this.el.select('input.form-control',true).first();
13699 return this.searchField;
13702 return this.el.select('input.form-control',true).first();
13706 onTickableFooterButtonClick : function(e, btn, el)
13708 e.preventDefault();
13710 this.lastItem = Roo.apply([], this.item);
13712 if(btn && btn.name == 'cancel'){
13713 this.tickItems = Roo.apply([], this.item);
13722 Roo.each(this.tickItems, function(o){
13730 validate : function()
13732 var v = this.getRawValue();
13735 v = this.getValue();
13738 if(this.disabled || this.allowBlank || v.length){
13743 this.markInvalid();
13747 tickableInputEl : function()
13749 if(!this.tickable || !this.editable){
13750 return this.inputEl();
13753 return this.inputEl().select('.roo-select2-search-field-input', true).first();
13757 getAutoCreateTouchView : function()
13762 cls: 'form-group' //input-group
13768 type : this.inputType,
13769 cls : 'form-control x-combo-noedit',
13770 autocomplete: 'new-password',
13771 placeholder : this.placeholder || '',
13776 input.name = this.name;
13780 input.cls += ' input-' + this.size;
13783 if (this.disabled) {
13784 input.disabled = true;
13795 inputblock.cls += ' input-group';
13797 inputblock.cn.unshift({
13799 cls : 'input-group-addon',
13804 if(this.removable && !this.multiple){
13805 inputblock.cls += ' roo-removable';
13807 inputblock.cn.push({
13810 cls : 'roo-combo-removable-btn close'
13814 if(this.hasFeedback && !this.allowBlank){
13816 inputblock.cls += ' has-feedback';
13818 inputblock.cn.push({
13820 cls: 'glyphicon form-control-feedback'
13827 inputblock.cls += (this.before) ? '' : ' input-group';
13829 inputblock.cn.push({
13831 cls : 'input-group-addon',
13842 cls: 'form-hidden-field'
13856 cls: 'form-hidden-field'
13860 cls: 'roo-select2-choices',
13864 cls: 'roo-select2-search-field',
13877 cls: 'roo-select2-container input-group',
13883 // if(!this.multiple && this.showToggleBtn){
13890 // if (this.caret != false) {
13893 // cls: 'fa fa-' + this.caret
13898 // combobox.cn.push({
13900 // cls : 'input-group-addon btn dropdown-toggle',
13905 // cls: 'combobox-clear',
13909 // cls: 'icon-remove'
13919 combobox.cls += ' roo-select2-container-multi';
13922 var align = this.labelAlign || this.parentLabelAlign();
13926 if(this.fieldLabel.length && this.labelWidth){
13928 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13929 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13934 cls : 'control-label ' + lw,
13935 html : this.fieldLabel
13947 var settings = this;
13949 ['xs','sm','md','lg'].map(function(size){
13950 if (settings[size]) {
13951 cfg.cls += ' col-' + size + '-' + settings[size];
13958 initTouchView : function()
13960 this.renderTouchView();
13962 this.touchViewEl.on('scroll', function(){
13963 this.el.dom.scrollTop = 0;
13966 this.originalValue = this.getValue();
13968 this.inputEl().on("touch", this.showTouchView, this);
13970 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13971 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13973 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13975 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13976 this.store.on('load', this.onTouchViewLoad, this);
13977 this.store.on('loadexception', this.onTouchViewLoadException, this);
13979 if(this.hiddenName){
13981 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13983 this.hiddenField.dom.value =
13984 this.hiddenValue !== undefined ? this.hiddenValue :
13985 this.value !== undefined ? this.value : '';
13987 this.el.dom.removeAttribute('name');
13988 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13992 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13993 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13996 if(this.removable && !this.multiple){
13997 var close = this.closeTriggerEl();
13999 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14000 close.on('click', this.removeBtnClick, this, close);
14004 * fix the bug in Safari iOS8
14006 this.inputEl().on("focus", function(e){
14007 document.activeElement.blur();
14015 renderTouchView : function()
14017 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14018 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14020 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14021 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14023 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14024 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14025 this.touchViewBodyEl.setStyle('overflow', 'auto');
14027 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14028 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14030 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14031 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14035 showTouchView : function()
14041 this.touchViewHeaderEl.hide();
14043 if(this.fieldLabel.length){
14044 this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14045 this.touchViewHeaderEl.show();
14048 this.touchViewEl.show();
14050 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14051 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14052 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14054 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14056 if(this.fieldLabel.length){
14057 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14060 this.touchViewBodyEl.setHeight(bodyHeight);
14064 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14066 this.touchViewEl.addClass('in');
14069 this.doTouchViewQuery();
14073 hideTouchView : function()
14075 this.touchViewEl.removeClass('in');
14079 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14081 this.touchViewEl.setStyle('display', 'none');
14086 setTouchViewValue : function()
14093 Roo.each(this.tickItems, function(o){
14098 this.hideTouchView();
14101 doTouchViewQuery : function()
14110 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14114 if(!this.alwaysQuery || this.mode == 'local'){
14115 this.onTouchViewLoad();
14122 onTouchViewBeforeLoad : function(combo,opts)
14128 onTouchViewLoad : function()
14130 if(this.store.getCount() < 1){
14131 this.onTouchViewEmptyResults();
14135 this.clearTouchView();
14137 var rawValue = this.getRawValue();
14139 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14141 this.tickItems = [];
14143 this.store.data.each(function(d, rowIndex){
14144 var row = this.touchViewListGroup.createChild(template);
14146 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14147 row.addClass(d.data.cls);
14150 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14153 html : d.data[this.displayField]
14156 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14157 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14160 row.removeClass('selected');
14161 if(!this.multiple && this.valueField &&
14162 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14165 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14166 row.addClass('selected');
14169 if(this.multiple && this.valueField &&
14170 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14174 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14175 this.tickItems.push(d.data);
14178 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14182 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14184 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14186 if(this.fieldLabel.length){
14187 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14190 var listHeight = this.touchViewListGroup.getHeight();
14194 if(firstChecked && listHeight > bodyHeight){
14195 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14200 onTouchViewLoadException : function()
14202 this.hideTouchView();
14205 onTouchViewEmptyResults : function()
14207 this.clearTouchView();
14209 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14211 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14215 clearTouchView : function()
14217 this.touchViewListGroup.dom.innerHTML = '';
14220 onTouchViewClick : function(e, el, o)
14222 e.preventDefault();
14225 var rowIndex = o.rowIndex;
14227 var r = this.store.getAt(rowIndex);
14229 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14231 if(!this.multiple){
14232 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14233 c.dom.removeAttribute('checked');
14236 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14238 this.setFromData(r.data);
14240 var close = this.closeTriggerEl();
14246 this.hideTouchView();
14248 this.fireEvent('select', this, r, rowIndex);
14253 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14254 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14255 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14259 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14260 this.addItem(r.data);
14261 this.tickItems.push(r.data);
14267 * @cfg {Boolean} grow
14271 * @cfg {Number} growMin
14275 * @cfg {Number} growMax
14284 Roo.apply(Roo.bootstrap.ComboBox, {
14288 cls: 'modal-header',
14310 cls: 'list-group-item',
14314 cls: 'roo-combobox-list-group-item-value'
14318 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14332 listItemCheckbox : {
14334 cls: 'list-group-item',
14338 cls: 'roo-combobox-list-group-item-value'
14342 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14358 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14363 cls: 'modal-footer',
14371 cls: 'col-xs-6 text-left',
14374 cls: 'btn btn-danger roo-touch-view-cancel',
14380 cls: 'col-xs-6 text-right',
14383 cls: 'btn btn-success roo-touch-view-ok',
14394 Roo.apply(Roo.bootstrap.ComboBox, {
14396 touchViewTemplate : {
14398 cls: 'modal fade roo-combobox-touch-view',
14402 cls: 'modal-dialog',
14403 style : 'position:fixed', // we have to fix position....
14407 cls: 'modal-content',
14409 Roo.bootstrap.ComboBox.header,
14410 Roo.bootstrap.ComboBox.body,
14411 Roo.bootstrap.ComboBox.footer
14420 * Ext JS Library 1.1.1
14421 * Copyright(c) 2006-2007, Ext JS, LLC.
14423 * Originally Released Under LGPL - original licence link has changed is not relivant.
14426 * <script type="text/javascript">
14431 * @extends Roo.util.Observable
14432 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14433 * This class also supports single and multi selection modes. <br>
14434 * Create a data model bound view:
14436 var store = new Roo.data.Store(...);
14438 var view = new Roo.View({
14440 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14442 singleSelect: true,
14443 selectedClass: "ydataview-selected",
14447 // listen for node click?
14448 view.on("click", function(vw, index, node, e){
14449 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14453 dataModel.load("foobar.xml");
14455 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14457 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14458 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14460 * Note: old style constructor is still suported (container, template, config)
14463 * Create a new View
14464 * @param {Object} config The config object
14467 Roo.View = function(config, depreciated_tpl, depreciated_config){
14469 this.parent = false;
14471 if (typeof(depreciated_tpl) == 'undefined') {
14472 // new way.. - universal constructor.
14473 Roo.apply(this, config);
14474 this.el = Roo.get(this.el);
14477 this.el = Roo.get(config);
14478 this.tpl = depreciated_tpl;
14479 Roo.apply(this, depreciated_config);
14481 this.wrapEl = this.el.wrap().wrap();
14482 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14485 if(typeof(this.tpl) == "string"){
14486 this.tpl = new Roo.Template(this.tpl);
14488 // support xtype ctors..
14489 this.tpl = new Roo.factory(this.tpl, Roo);
14493 this.tpl.compile();
14498 * @event beforeclick
14499 * Fires before a click is processed. Returns false to cancel the default action.
14500 * @param {Roo.View} this
14501 * @param {Number} index The index of the target node
14502 * @param {HTMLElement} node The target node
14503 * @param {Roo.EventObject} e The raw event object
14505 "beforeclick" : true,
14508 * Fires when a template node is clicked.
14509 * @param {Roo.View} this
14510 * @param {Number} index The index of the target node
14511 * @param {HTMLElement} node The target node
14512 * @param {Roo.EventObject} e The raw event object
14517 * Fires when a template node is double clicked.
14518 * @param {Roo.View} this
14519 * @param {Number} index The index of the target node
14520 * @param {HTMLElement} node The target node
14521 * @param {Roo.EventObject} e The raw event object
14525 * @event contextmenu
14526 * Fires when a template node is right clicked.
14527 * @param {Roo.View} this
14528 * @param {Number} index The index of the target node
14529 * @param {HTMLElement} node The target node
14530 * @param {Roo.EventObject} e The raw event object
14532 "contextmenu" : true,
14534 * @event selectionchange
14535 * Fires when the selected nodes change.
14536 * @param {Roo.View} this
14537 * @param {Array} selections Array of the selected nodes
14539 "selectionchange" : true,
14542 * @event beforeselect
14543 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14544 * @param {Roo.View} this
14545 * @param {HTMLElement} node The node to be selected
14546 * @param {Array} selections Array of currently selected nodes
14548 "beforeselect" : true,
14550 * @event preparedata
14551 * Fires on every row to render, to allow you to change the data.
14552 * @param {Roo.View} this
14553 * @param {Object} data to be rendered (change this)
14555 "preparedata" : true
14563 "click": this.onClick,
14564 "dblclick": this.onDblClick,
14565 "contextmenu": this.onContextMenu,
14569 this.selections = [];
14571 this.cmp = new Roo.CompositeElementLite([]);
14573 this.store = Roo.factory(this.store, Roo.data);
14574 this.setStore(this.store, true);
14577 if ( this.footer && this.footer.xtype) {
14579 var fctr = this.wrapEl.appendChild(document.createElement("div"));
14581 this.footer.dataSource = this.store;
14582 this.footer.container = fctr;
14583 this.footer = Roo.factory(this.footer, Roo);
14584 fctr.insertFirst(this.el);
14586 // this is a bit insane - as the paging toolbar seems to detach the el..
14587 // dom.parentNode.parentNode.parentNode
14588 // they get detached?
14592 Roo.View.superclass.constructor.call(this);
14597 Roo.extend(Roo.View, Roo.util.Observable, {
14600 * @cfg {Roo.data.Store} store Data store to load data from.
14605 * @cfg {String|Roo.Element} el The container element.
14610 * @cfg {String|Roo.Template} tpl The template used by this View
14614 * @cfg {String} dataName the named area of the template to use as the data area
14615 * Works with domtemplates roo-name="name"
14619 * @cfg {String} selectedClass The css class to add to selected nodes
14621 selectedClass : "x-view-selected",
14623 * @cfg {String} emptyText The empty text to show when nothing is loaded.
14628 * @cfg {String} text to display on mask (default Loading)
14632 * @cfg {Boolean} multiSelect Allow multiple selection
14634 multiSelect : false,
14636 * @cfg {Boolean} singleSelect Allow single selection
14638 singleSelect: false,
14641 * @cfg {Boolean} toggleSelect - selecting
14643 toggleSelect : false,
14646 * @cfg {Boolean} tickable - selecting
14651 * Returns the element this view is bound to.
14652 * @return {Roo.Element}
14654 getEl : function(){
14655 return this.wrapEl;
14661 * Refreshes the view. - called by datachanged on the store. - do not call directly.
14663 refresh : function(){
14664 //Roo.log('refresh');
14667 // if we are using something like 'domtemplate', then
14668 // the what gets used is:
14669 // t.applySubtemplate(NAME, data, wrapping data..)
14670 // the outer template then get' applied with
14671 // the store 'extra data'
14672 // and the body get's added to the
14673 // roo-name="data" node?
14674 // <span class='roo-tpl-{name}'></span> ?????
14678 this.clearSelections();
14679 this.el.update("");
14681 var records = this.store.getRange();
14682 if(records.length < 1) {
14684 // is this valid?? = should it render a template??
14686 this.el.update(this.emptyText);
14690 if (this.dataName) {
14691 this.el.update(t.apply(this.store.meta)); //????
14692 el = this.el.child('.roo-tpl-' + this.dataName);
14695 for(var i = 0, len = records.length; i < len; i++){
14696 var data = this.prepareData(records[i].data, i, records[i]);
14697 this.fireEvent("preparedata", this, data, i, records[i]);
14699 var d = Roo.apply({}, data);
14702 Roo.apply(d, {'roo-id' : Roo.id()});
14706 Roo.each(this.parent.item, function(item){
14707 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14710 Roo.apply(d, {'roo-data-checked' : 'checked'});
14714 html[html.length] = Roo.util.Format.trim(
14716 t.applySubtemplate(this.dataName, d, this.store.meta) :
14723 el.update(html.join(""));
14724 this.nodes = el.dom.childNodes;
14725 this.updateIndexes(0);
14730 * Function to override to reformat the data that is sent to
14731 * the template for each node.
14732 * DEPRICATED - use the preparedata event handler.
14733 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14734 * a JSON object for an UpdateManager bound view).
14736 prepareData : function(data, index, record)
14738 this.fireEvent("preparedata", this, data, index, record);
14742 onUpdate : function(ds, record){
14743 // Roo.log('on update');
14744 this.clearSelections();
14745 var index = this.store.indexOf(record);
14746 var n = this.nodes[index];
14747 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14748 n.parentNode.removeChild(n);
14749 this.updateIndexes(index, index);
14755 onAdd : function(ds, records, index)
14757 //Roo.log(['on Add', ds, records, index] );
14758 this.clearSelections();
14759 if(this.nodes.length == 0){
14763 var n = this.nodes[index];
14764 for(var i = 0, len = records.length; i < len; i++){
14765 var d = this.prepareData(records[i].data, i, records[i]);
14767 this.tpl.insertBefore(n, d);
14770 this.tpl.append(this.el, d);
14773 this.updateIndexes(index);
14776 onRemove : function(ds, record, index){
14777 // Roo.log('onRemove');
14778 this.clearSelections();
14779 var el = this.dataName ?
14780 this.el.child('.roo-tpl-' + this.dataName) :
14783 el.dom.removeChild(this.nodes[index]);
14784 this.updateIndexes(index);
14788 * Refresh an individual node.
14789 * @param {Number} index
14791 refreshNode : function(index){
14792 this.onUpdate(this.store, this.store.getAt(index));
14795 updateIndexes : function(startIndex, endIndex){
14796 var ns = this.nodes;
14797 startIndex = startIndex || 0;
14798 endIndex = endIndex || ns.length - 1;
14799 for(var i = startIndex; i <= endIndex; i++){
14800 ns[i].nodeIndex = i;
14805 * Changes the data store this view uses and refresh the view.
14806 * @param {Store} store
14808 setStore : function(store, initial){
14809 if(!initial && this.store){
14810 this.store.un("datachanged", this.refresh);
14811 this.store.un("add", this.onAdd);
14812 this.store.un("remove", this.onRemove);
14813 this.store.un("update", this.onUpdate);
14814 this.store.un("clear", this.refresh);
14815 this.store.un("beforeload", this.onBeforeLoad);
14816 this.store.un("load", this.onLoad);
14817 this.store.un("loadexception", this.onLoad);
14821 store.on("datachanged", this.refresh, this);
14822 store.on("add", this.onAdd, this);
14823 store.on("remove", this.onRemove, this);
14824 store.on("update", this.onUpdate, this);
14825 store.on("clear", this.refresh, this);
14826 store.on("beforeload", this.onBeforeLoad, this);
14827 store.on("load", this.onLoad, this);
14828 store.on("loadexception", this.onLoad, this);
14836 * onbeforeLoad - masks the loading area.
14839 onBeforeLoad : function(store,opts)
14841 //Roo.log('onBeforeLoad');
14843 this.el.update("");
14845 this.el.mask(this.mask ? this.mask : "Loading" );
14847 onLoad : function ()
14854 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14855 * @param {HTMLElement} node
14856 * @return {HTMLElement} The template node
14858 findItemFromChild : function(node){
14859 var el = this.dataName ?
14860 this.el.child('.roo-tpl-' + this.dataName,true) :
14863 if(!node || node.parentNode == el){
14866 var p = node.parentNode;
14867 while(p && p != el){
14868 if(p.parentNode == el){
14877 onClick : function(e){
14878 var item = this.findItemFromChild(e.getTarget());
14880 var index = this.indexOf(item);
14881 if(this.onItemClick(item, index, e) !== false){
14882 this.fireEvent("click", this, index, item, e);
14885 this.clearSelections();
14890 onContextMenu : function(e){
14891 var item = this.findItemFromChild(e.getTarget());
14893 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14898 onDblClick : function(e){
14899 var item = this.findItemFromChild(e.getTarget());
14901 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14905 onItemClick : function(item, index, e)
14907 if(this.fireEvent("beforeclick", this, index, item, e) === false){
14910 if (this.toggleSelect) {
14911 var m = this.isSelected(item) ? 'unselect' : 'select';
14914 _t[m](item, true, false);
14917 if(this.multiSelect || this.singleSelect){
14918 if(this.multiSelect && e.shiftKey && this.lastSelection){
14919 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14921 this.select(item, this.multiSelect && e.ctrlKey);
14922 this.lastSelection = item;
14925 if(!this.tickable){
14926 e.preventDefault();
14934 * Get the number of selected nodes.
14937 getSelectionCount : function(){
14938 return this.selections.length;
14942 * Get the currently selected nodes.
14943 * @return {Array} An array of HTMLElements
14945 getSelectedNodes : function(){
14946 return this.selections;
14950 * Get the indexes of the selected nodes.
14953 getSelectedIndexes : function(){
14954 var indexes = [], s = this.selections;
14955 for(var i = 0, len = s.length; i < len; i++){
14956 indexes.push(s[i].nodeIndex);
14962 * Clear all selections
14963 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14965 clearSelections : function(suppressEvent){
14966 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14967 this.cmp.elements = this.selections;
14968 this.cmp.removeClass(this.selectedClass);
14969 this.selections = [];
14970 if(!suppressEvent){
14971 this.fireEvent("selectionchange", this, this.selections);
14977 * Returns true if the passed node is selected
14978 * @param {HTMLElement/Number} node The node or node index
14979 * @return {Boolean}
14981 isSelected : function(node){
14982 var s = this.selections;
14986 node = this.getNode(node);
14987 return s.indexOf(node) !== -1;
14992 * @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
14993 * @param {Boolean} keepExisting (optional) true to keep existing selections
14994 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14996 select : function(nodeInfo, keepExisting, suppressEvent){
14997 if(nodeInfo instanceof Array){
14999 this.clearSelections(true);
15001 for(var i = 0, len = nodeInfo.length; i < len; i++){
15002 this.select(nodeInfo[i], true, true);
15006 var node = this.getNode(nodeInfo);
15007 if(!node || this.isSelected(node)){
15008 return; // already selected.
15011 this.clearSelections(true);
15014 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15015 Roo.fly(node).addClass(this.selectedClass);
15016 this.selections.push(node);
15017 if(!suppressEvent){
15018 this.fireEvent("selectionchange", this, this.selections);
15026 * @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
15027 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15028 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15030 unselect : function(nodeInfo, keepExisting, suppressEvent)
15032 if(nodeInfo instanceof Array){
15033 Roo.each(this.selections, function(s) {
15034 this.unselect(s, nodeInfo);
15038 var node = this.getNode(nodeInfo);
15039 if(!node || !this.isSelected(node)){
15040 //Roo.log("not selected");
15041 return; // not selected.
15045 Roo.each(this.selections, function(s) {
15047 Roo.fly(node).removeClass(this.selectedClass);
15054 this.selections= ns;
15055 this.fireEvent("selectionchange", this, this.selections);
15059 * Gets a template node.
15060 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15061 * @return {HTMLElement} The node or null if it wasn't found
15063 getNode : function(nodeInfo){
15064 if(typeof nodeInfo == "string"){
15065 return document.getElementById(nodeInfo);
15066 }else if(typeof nodeInfo == "number"){
15067 return this.nodes[nodeInfo];
15073 * Gets a range template nodes.
15074 * @param {Number} startIndex
15075 * @param {Number} endIndex
15076 * @return {Array} An array of nodes
15078 getNodes : function(start, end){
15079 var ns = this.nodes;
15080 start = start || 0;
15081 end = typeof end == "undefined" ? ns.length - 1 : end;
15084 for(var i = start; i <= end; i++){
15088 for(var i = start; i >= end; i--){
15096 * Finds the index of the passed node
15097 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15098 * @return {Number} The index of the node or -1
15100 indexOf : function(node){
15101 node = this.getNode(node);
15102 if(typeof node.nodeIndex == "number"){
15103 return node.nodeIndex;
15105 var ns = this.nodes;
15106 for(var i = 0, len = ns.length; i < len; i++){
15117 * based on jquery fullcalendar
15121 Roo.bootstrap = Roo.bootstrap || {};
15123 * @class Roo.bootstrap.Calendar
15124 * @extends Roo.bootstrap.Component
15125 * Bootstrap Calendar class
15126 * @cfg {Boolean} loadMask (true|false) default false
15127 * @cfg {Object} header generate the user specific header of the calendar, default false
15130 * Create a new Container
15131 * @param {Object} config The config object
15136 Roo.bootstrap.Calendar = function(config){
15137 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15141 * Fires when a date is selected
15142 * @param {DatePicker} this
15143 * @param {Date} date The selected date
15147 * @event monthchange
15148 * Fires when the displayed month changes
15149 * @param {DatePicker} this
15150 * @param {Date} date The selected month
15152 'monthchange': true,
15154 * @event evententer
15155 * Fires when mouse over an event
15156 * @param {Calendar} this
15157 * @param {event} Event
15159 'evententer': true,
15161 * @event eventleave
15162 * Fires when the mouse leaves an
15163 * @param {Calendar} this
15166 'eventleave': true,
15168 * @event eventclick
15169 * Fires when the mouse click an
15170 * @param {Calendar} this
15179 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15182 * @cfg {Number} startDay
15183 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15191 getAutoCreate : function(){
15194 var fc_button = function(name, corner, style, content ) {
15195 return Roo.apply({},{
15197 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15199 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15202 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15213 style : 'width:100%',
15220 cls : 'fc-header-left',
15222 fc_button('prev', 'left', 'arrow', '‹' ),
15223 fc_button('next', 'right', 'arrow', '›' ),
15224 { tag: 'span', cls: 'fc-header-space' },
15225 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15233 cls : 'fc-header-center',
15237 cls: 'fc-header-title',
15240 html : 'month / year'
15248 cls : 'fc-header-right',
15250 /* fc_button('month', 'left', '', 'month' ),
15251 fc_button('week', '', '', 'week' ),
15252 fc_button('day', 'right', '', 'day' )
15264 header = this.header;
15267 var cal_heads = function() {
15269 // fixme - handle this.
15271 for (var i =0; i < Date.dayNames.length; i++) {
15272 var d = Date.dayNames[i];
15275 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15276 html : d.substring(0,3)
15280 ret[0].cls += ' fc-first';
15281 ret[6].cls += ' fc-last';
15284 var cal_cell = function(n) {
15287 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15292 cls: 'fc-day-number',
15296 cls: 'fc-day-content',
15300 style: 'position: relative;' // height: 17px;
15312 var cal_rows = function() {
15315 for (var r = 0; r < 6; r++) {
15322 for (var i =0; i < Date.dayNames.length; i++) {
15323 var d = Date.dayNames[i];
15324 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15327 row.cn[0].cls+=' fc-first';
15328 row.cn[0].cn[0].style = 'min-height:90px';
15329 row.cn[6].cls+=' fc-last';
15333 ret[0].cls += ' fc-first';
15334 ret[4].cls += ' fc-prev-last';
15335 ret[5].cls += ' fc-last';
15342 cls: 'fc-border-separate',
15343 style : 'width:100%',
15351 cls : 'fc-first fc-last',
15369 cls : 'fc-content',
15370 style : "position: relative;",
15373 cls : 'fc-view fc-view-month fc-grid',
15374 style : 'position: relative',
15375 unselectable : 'on',
15378 cls : 'fc-event-container',
15379 style : 'position:absolute;z-index:8;top:0;left:0;'
15397 initEvents : function()
15400 throw "can not find store for calendar";
15406 style: "text-align:center",
15410 style: "background-color:white;width:50%;margin:250 auto",
15414 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15425 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15427 var size = this.el.select('.fc-content', true).first().getSize();
15428 this.maskEl.setSize(size.width, size.height);
15429 this.maskEl.enableDisplayMode("block");
15430 if(!this.loadMask){
15431 this.maskEl.hide();
15434 this.store = Roo.factory(this.store, Roo.data);
15435 this.store.on('load', this.onLoad, this);
15436 this.store.on('beforeload', this.onBeforeLoad, this);
15440 this.cells = this.el.select('.fc-day',true);
15441 //Roo.log(this.cells);
15442 this.textNodes = this.el.query('.fc-day-number');
15443 this.cells.addClassOnOver('fc-state-hover');
15445 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15446 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15447 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15448 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15450 this.on('monthchange', this.onMonthChange, this);
15452 this.update(new Date().clearTime());
15455 resize : function() {
15456 var sz = this.el.getSize();
15458 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15459 this.el.select('.fc-day-content div',true).setHeight(34);
15464 showPrevMonth : function(e){
15465 this.update(this.activeDate.add("mo", -1));
15467 showToday : function(e){
15468 this.update(new Date().clearTime());
15471 showNextMonth : function(e){
15472 this.update(this.activeDate.add("mo", 1));
15476 showPrevYear : function(){
15477 this.update(this.activeDate.add("y", -1));
15481 showNextYear : function(){
15482 this.update(this.activeDate.add("y", 1));
15487 update : function(date)
15489 var vd = this.activeDate;
15490 this.activeDate = date;
15491 // if(vd && this.el){
15492 // var t = date.getTime();
15493 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15494 // Roo.log('using add remove');
15496 // this.fireEvent('monthchange', this, date);
15498 // this.cells.removeClass("fc-state-highlight");
15499 // this.cells.each(function(c){
15500 // if(c.dateValue == t){
15501 // c.addClass("fc-state-highlight");
15502 // setTimeout(function(){
15503 // try{c.dom.firstChild.focus();}catch(e){}
15513 var days = date.getDaysInMonth();
15515 var firstOfMonth = date.getFirstDateOfMonth();
15516 var startingPos = firstOfMonth.getDay()-this.startDay;
15518 if(startingPos < this.startDay){
15522 var pm = date.add(Date.MONTH, -1);
15523 var prevStart = pm.getDaysInMonth()-startingPos;
15525 this.cells = this.el.select('.fc-day',true);
15526 this.textNodes = this.el.query('.fc-day-number');
15527 this.cells.addClassOnOver('fc-state-hover');
15529 var cells = this.cells.elements;
15530 var textEls = this.textNodes;
15532 Roo.each(cells, function(cell){
15533 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15536 days += startingPos;
15538 // convert everything to numbers so it's fast
15539 var day = 86400000;
15540 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15543 //Roo.log(prevStart);
15545 var today = new Date().clearTime().getTime();
15546 var sel = date.clearTime().getTime();
15547 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15548 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15549 var ddMatch = this.disabledDatesRE;
15550 var ddText = this.disabledDatesText;
15551 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15552 var ddaysText = this.disabledDaysText;
15553 var format = this.format;
15555 var setCellClass = function(cal, cell){
15559 //Roo.log('set Cell Class');
15561 var t = d.getTime();
15565 cell.dateValue = t;
15567 cell.className += " fc-today";
15568 cell.className += " fc-state-highlight";
15569 cell.title = cal.todayText;
15572 // disable highlight in other month..
15573 //cell.className += " fc-state-highlight";
15578 cell.className = " fc-state-disabled";
15579 cell.title = cal.minText;
15583 cell.className = " fc-state-disabled";
15584 cell.title = cal.maxText;
15588 if(ddays.indexOf(d.getDay()) != -1){
15589 cell.title = ddaysText;
15590 cell.className = " fc-state-disabled";
15593 if(ddMatch && format){
15594 var fvalue = d.dateFormat(format);
15595 if(ddMatch.test(fvalue)){
15596 cell.title = ddText.replace("%0", fvalue);
15597 cell.className = " fc-state-disabled";
15601 if (!cell.initialClassName) {
15602 cell.initialClassName = cell.dom.className;
15605 cell.dom.className = cell.initialClassName + ' ' + cell.className;
15610 for(; i < startingPos; i++) {
15611 textEls[i].innerHTML = (++prevStart);
15612 d.setDate(d.getDate()+1);
15614 cells[i].className = "fc-past fc-other-month";
15615 setCellClass(this, cells[i]);
15620 for(; i < days; i++){
15621 intDay = i - startingPos + 1;
15622 textEls[i].innerHTML = (intDay);
15623 d.setDate(d.getDate()+1);
15625 cells[i].className = ''; // "x-date-active";
15626 setCellClass(this, cells[i]);
15630 for(; i < 42; i++) {
15631 textEls[i].innerHTML = (++extraDays);
15632 d.setDate(d.getDate()+1);
15634 cells[i].className = "fc-future fc-other-month";
15635 setCellClass(this, cells[i]);
15638 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15640 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15642 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15643 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15645 if(totalRows != 6){
15646 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15647 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15650 this.fireEvent('monthchange', this, date);
15654 if(!this.internalRender){
15655 var main = this.el.dom.firstChild;
15656 var w = main.offsetWidth;
15657 this.el.setWidth(w + this.el.getBorderWidth("lr"));
15658 Roo.fly(main).setWidth(w);
15659 this.internalRender = true;
15660 // opera does not respect the auto grow header center column
15661 // then, after it gets a width opera refuses to recalculate
15662 // without a second pass
15663 if(Roo.isOpera && !this.secondPass){
15664 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15665 this.secondPass = true;
15666 this.update.defer(10, this, [date]);
15673 findCell : function(dt) {
15674 dt = dt.clearTime().getTime();
15676 this.cells.each(function(c){
15677 //Roo.log("check " +c.dateValue + '?=' + dt);
15678 if(c.dateValue == dt){
15688 findCells : function(ev) {
15689 var s = ev.start.clone().clearTime().getTime();
15691 var e= ev.end.clone().clearTime().getTime();
15694 this.cells.each(function(c){
15695 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15697 if(c.dateValue > e){
15700 if(c.dateValue < s){
15709 // findBestRow: function(cells)
15713 // for (var i =0 ; i < cells.length;i++) {
15714 // ret = Math.max(cells[i].rows || 0,ret);
15721 addItem : function(ev)
15723 // look for vertical location slot in
15724 var cells = this.findCells(ev);
15726 // ev.row = this.findBestRow(cells);
15728 // work out the location.
15732 for(var i =0; i < cells.length; i++) {
15734 cells[i].row = cells[0].row;
15737 cells[i].row = cells[i].row + 1;
15747 if (crow.start.getY() == cells[i].getY()) {
15749 crow.end = cells[i];
15766 cells[0].events.push(ev);
15768 this.calevents.push(ev);
15771 clearEvents: function() {
15773 if(!this.calevents){
15777 Roo.each(this.cells.elements, function(c){
15783 Roo.each(this.calevents, function(e) {
15784 Roo.each(e.els, function(el) {
15785 el.un('mouseenter' ,this.onEventEnter, this);
15786 el.un('mouseleave' ,this.onEventLeave, this);
15791 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15797 renderEvents: function()
15801 this.cells.each(function(c) {
15810 if(c.row != c.events.length){
15811 r = 4 - (4 - (c.row - c.events.length));
15814 c.events = ev.slice(0, r);
15815 c.more = ev.slice(r);
15817 if(c.more.length && c.more.length == 1){
15818 c.events.push(c.more.pop());
15821 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15825 this.cells.each(function(c) {
15827 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15830 for (var e = 0; e < c.events.length; e++){
15831 var ev = c.events[e];
15832 var rows = ev.rows;
15834 for(var i = 0; i < rows.length; i++) {
15836 // how many rows should it span..
15839 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15840 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15842 unselectable : "on",
15845 cls: 'fc-event-inner',
15849 // cls: 'fc-event-time',
15850 // html : cells.length > 1 ? '' : ev.time
15854 cls: 'fc-event-title',
15855 html : String.format('{0}', ev.title)
15862 cls: 'ui-resizable-handle ui-resizable-e',
15863 html : '  '
15870 cfg.cls += ' fc-event-start';
15872 if ((i+1) == rows.length) {
15873 cfg.cls += ' fc-event-end';
15876 var ctr = _this.el.select('.fc-event-container',true).first();
15877 var cg = ctr.createChild(cfg);
15879 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15880 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15882 var r = (c.more.length) ? 1 : 0;
15883 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
15884 cg.setWidth(ebox.right - sbox.x -2);
15886 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15887 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15888 cg.on('click', _this.onEventClick, _this, ev);
15899 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15900 style : 'position: absolute',
15901 unselectable : "on",
15904 cls: 'fc-event-inner',
15908 cls: 'fc-event-title',
15916 cls: 'ui-resizable-handle ui-resizable-e',
15917 html : '  '
15923 var ctr = _this.el.select('.fc-event-container',true).first();
15924 var cg = ctr.createChild(cfg);
15926 var sbox = c.select('.fc-day-content',true).first().getBox();
15927 var ebox = c.select('.fc-day-content',true).first().getBox();
15929 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
15930 cg.setWidth(ebox.right - sbox.x -2);
15932 cg.on('click', _this.onMoreEventClick, _this, c.more);
15942 onEventEnter: function (e, el,event,d) {
15943 this.fireEvent('evententer', this, el, event);
15946 onEventLeave: function (e, el,event,d) {
15947 this.fireEvent('eventleave', this, el, event);
15950 onEventClick: function (e, el,event,d) {
15951 this.fireEvent('eventclick', this, el, event);
15954 onMonthChange: function () {
15958 onMoreEventClick: function(e, el, more)
15962 this.calpopover.placement = 'right';
15963 this.calpopover.setTitle('More');
15965 this.calpopover.setContent('');
15967 var ctr = this.calpopover.el.select('.popover-content', true).first();
15969 Roo.each(more, function(m){
15971 cls : 'fc-event-hori fc-event-draggable',
15974 var cg = ctr.createChild(cfg);
15976 cg.on('click', _this.onEventClick, _this, m);
15979 this.calpopover.show(el);
15984 onLoad: function ()
15986 this.calevents = [];
15989 if(this.store.getCount() > 0){
15990 this.store.data.each(function(d){
15993 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15994 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15995 time : d.data.start_time,
15996 title : d.data.title,
15997 description : d.data.description,
15998 venue : d.data.venue
16003 this.renderEvents();
16005 if(this.calevents.length && this.loadMask){
16006 this.maskEl.hide();
16010 onBeforeLoad: function()
16012 this.clearEvents();
16014 this.maskEl.show();
16028 * @class Roo.bootstrap.Popover
16029 * @extends Roo.bootstrap.Component
16030 * Bootstrap Popover class
16031 * @cfg {String} html contents of the popover (or false to use children..)
16032 * @cfg {String} title of popover (or false to hide)
16033 * @cfg {String} placement how it is placed
16034 * @cfg {String} trigger click || hover (or false to trigger manually)
16035 * @cfg {String} over what (parent or false to trigger manually.)
16036 * @cfg {Number} delay - delay before showing
16039 * Create a new Popover
16040 * @param {Object} config The config object
16043 Roo.bootstrap.Popover = function(config){
16044 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16050 * After the popover show
16052 * @param {Roo.bootstrap.Popover} this
16057 * After the popover hide
16059 * @param {Roo.bootstrap.Popover} this
16065 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16067 title: 'Fill in a title',
16070 placement : 'right',
16071 trigger : 'hover', // hover
16077 can_build_overlaid : false,
16079 getChildContainer : function()
16081 return this.el.select('.popover-content',true).first();
16084 getAutoCreate : function(){
16087 cls : 'popover roo-dynamic',
16088 style: 'display:block',
16094 cls : 'popover-inner',
16098 cls: 'popover-title',
16102 cls : 'popover-content',
16113 setTitle: function(str)
16116 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16118 setContent: function(str)
16121 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16123 // as it get's added to the bottom of the page.
16124 onRender : function(ct, position)
16126 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16128 var cfg = Roo.apply({}, this.getAutoCreate());
16132 cfg.cls += ' ' + this.cls;
16135 cfg.style = this.style;
16137 //Roo.log("adding to ");
16138 this.el = Roo.get(document.body).createChild(cfg, position);
16139 // Roo.log(this.el);
16144 initEvents : function()
16146 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16147 this.el.enableDisplayMode('block');
16149 if (this.over === false) {
16152 if (this.triggers === false) {
16155 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16156 var triggers = this.trigger ? this.trigger.split(' ') : [];
16157 Roo.each(triggers, function(trigger) {
16159 if (trigger == 'click') {
16160 on_el.on('click', this.toggle, this);
16161 } else if (trigger != 'manual') {
16162 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16163 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16165 on_el.on(eventIn ,this.enter, this);
16166 on_el.on(eventOut, this.leave, this);
16177 toggle : function () {
16178 this.hoverState == 'in' ? this.leave() : this.enter();
16181 enter : function () {
16183 clearTimeout(this.timeout);
16185 this.hoverState = 'in';
16187 if (!this.delay || !this.delay.show) {
16192 this.timeout = setTimeout(function () {
16193 if (_t.hoverState == 'in') {
16196 }, this.delay.show)
16199 leave : function() {
16200 clearTimeout(this.timeout);
16202 this.hoverState = 'out';
16204 if (!this.delay || !this.delay.hide) {
16209 this.timeout = setTimeout(function () {
16210 if (_t.hoverState == 'out') {
16213 }, this.delay.hide)
16216 show : function (on_el)
16219 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16223 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16224 if (this.html !== false) {
16225 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16227 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16228 if (!this.title.length) {
16229 this.el.select('.popover-title',true).hide();
16232 var placement = typeof this.placement == 'function' ?
16233 this.placement.call(this, this.el, on_el) :
16236 var autoToken = /\s?auto?\s?/i;
16237 var autoPlace = autoToken.test(placement);
16239 placement = placement.replace(autoToken, '') || 'top';
16243 //this.el.setXY([0,0]);
16245 this.el.dom.style.display='block';
16246 this.el.addClass(placement);
16248 //this.el.appendTo(on_el);
16250 var p = this.getPosition();
16251 var box = this.el.getBox();
16256 var align = Roo.bootstrap.Popover.alignment[placement];
16257 this.el.alignTo(on_el, align[0],align[1]);
16258 //var arrow = this.el.select('.arrow',true).first();
16259 //arrow.set(align[2],
16261 this.el.addClass('in');
16264 if (this.el.hasClass('fade')) {
16268 this.hoverState = 'in';
16270 this.fireEvent('show', this);
16275 this.el.setXY([0,0]);
16276 this.el.removeClass('in');
16278 this.hoverState = null;
16280 this.fireEvent('hide', this);
16285 Roo.bootstrap.Popover.alignment = {
16286 'left' : ['r-l', [-10,0], 'right'],
16287 'right' : ['l-r', [10,0], 'left'],
16288 'bottom' : ['t-b', [0,10], 'top'],
16289 'top' : [ 'b-t', [0,-10], 'bottom']
16300 * @class Roo.bootstrap.Progress
16301 * @extends Roo.bootstrap.Component
16302 * Bootstrap Progress class
16303 * @cfg {Boolean} striped striped of the progress bar
16304 * @cfg {Boolean} active animated of the progress bar
16308 * Create a new Progress
16309 * @param {Object} config The config object
16312 Roo.bootstrap.Progress = function(config){
16313 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16316 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16321 getAutoCreate : function(){
16329 cfg.cls += ' progress-striped';
16333 cfg.cls += ' active';
16352 * @class Roo.bootstrap.ProgressBar
16353 * @extends Roo.bootstrap.Component
16354 * Bootstrap ProgressBar class
16355 * @cfg {Number} aria_valuenow aria-value now
16356 * @cfg {Number} aria_valuemin aria-value min
16357 * @cfg {Number} aria_valuemax aria-value max
16358 * @cfg {String} label label for the progress bar
16359 * @cfg {String} panel (success | info | warning | danger )
16360 * @cfg {String} role role of the progress bar
16361 * @cfg {String} sr_only text
16365 * Create a new ProgressBar
16366 * @param {Object} config The config object
16369 Roo.bootstrap.ProgressBar = function(config){
16370 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16373 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16377 aria_valuemax : 100,
16383 getAutoCreate : function()
16388 cls: 'progress-bar',
16389 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16401 cfg.role = this.role;
16404 if(this.aria_valuenow){
16405 cfg['aria-valuenow'] = this.aria_valuenow;
16408 if(this.aria_valuemin){
16409 cfg['aria-valuemin'] = this.aria_valuemin;
16412 if(this.aria_valuemax){
16413 cfg['aria-valuemax'] = this.aria_valuemax;
16416 if(this.label && !this.sr_only){
16417 cfg.html = this.label;
16421 cfg.cls += ' progress-bar-' + this.panel;
16427 update : function(aria_valuenow)
16429 this.aria_valuenow = aria_valuenow;
16431 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16446 * @class Roo.bootstrap.TabGroup
16447 * @extends Roo.bootstrap.Column
16448 * Bootstrap Column class
16449 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16450 * @cfg {Boolean} carousel true to make the group behave like a carousel
16451 * @cfg {Boolean} bullets show bullets for the panels
16452 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16453 * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16454 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16455 * @cfg {Boolean} showarrow (true|false) show arrow default true
16458 * Create a new TabGroup
16459 * @param {Object} config The config object
16462 Roo.bootstrap.TabGroup = function(config){
16463 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16465 this.navId = Roo.id();
16468 Roo.bootstrap.TabGroup.register(this);
16472 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16475 transition : false,
16480 slideOnTouch : false,
16483 getAutoCreate : function()
16485 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16487 cfg.cls += ' tab-content';
16489 if (this.carousel) {
16490 cfg.cls += ' carousel slide';
16493 cls : 'carousel-inner',
16497 if(this.bullets && !Roo.isTouch){
16500 cls : 'carousel-bullets',
16504 if(this.bullets_cls){
16505 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16512 cfg.cn[0].cn.push(bullets);
16515 if(this.showarrow){
16516 cfg.cn[0].cn.push({
16518 class : 'carousel-arrow',
16522 class : 'carousel-prev',
16526 class : 'fa fa-chevron-left'
16532 class : 'carousel-next',
16536 class : 'fa fa-chevron-right'
16549 initEvents: function()
16551 if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16552 this.el.on("touchstart", this.onTouchStart, this);
16555 if(this.autoslide){
16558 this.slideFn = window.setInterval(function() {
16559 _this.showPanelNext();
16563 if(this.showarrow){
16564 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16565 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16571 onTouchStart : function(e, el, o)
16573 if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16577 this.showPanelNext();
16580 getChildContainer : function()
16582 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16586 * register a Navigation item
16587 * @param {Roo.bootstrap.NavItem} the navitem to add
16589 register : function(item)
16591 this.tabs.push( item);
16592 item.navId = this.navId; // not really needed..
16597 getActivePanel : function()
16600 Roo.each(this.tabs, function(t) {
16610 getPanelByName : function(n)
16613 Roo.each(this.tabs, function(t) {
16614 if (t.tabId == n) {
16622 indexOfPanel : function(p)
16625 Roo.each(this.tabs, function(t,i) {
16626 if (t.tabId == p.tabId) {
16635 * show a specific panel
16636 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16637 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16639 showPanel : function (pan)
16641 if(this.transition || typeof(pan) == 'undefined'){
16642 Roo.log("waiting for the transitionend");
16646 if (typeof(pan) == 'number') {
16647 pan = this.tabs[pan];
16650 if (typeof(pan) == 'string') {
16651 pan = this.getPanelByName(pan);
16654 var cur = this.getActivePanel();
16657 Roo.log('pan or acitve pan is undefined');
16661 if (pan.tabId == this.getActivePanel().tabId) {
16665 if (false === cur.fireEvent('beforedeactivate')) {
16669 if(this.bullets > 0 && !Roo.isTouch){
16670 this.setActiveBullet(this.indexOfPanel(pan));
16673 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16675 this.transition = true;
16676 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
16677 var lr = dir == 'next' ? 'left' : 'right';
16678 pan.el.addClass(dir); // or prev
16679 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16680 cur.el.addClass(lr); // or right
16681 pan.el.addClass(lr);
16684 cur.el.on('transitionend', function() {
16685 Roo.log("trans end?");
16687 pan.el.removeClass([lr,dir]);
16688 pan.setActive(true);
16690 cur.el.removeClass([lr]);
16691 cur.setActive(false);
16693 _this.transition = false;
16695 }, this, { single: true } );
16700 cur.setActive(false);
16701 pan.setActive(true);
16706 showPanelNext : function()
16708 var i = this.indexOfPanel(this.getActivePanel());
16710 if (i >= this.tabs.length - 1 && !this.autoslide) {
16714 if (i >= this.tabs.length - 1 && this.autoslide) {
16718 this.showPanel(this.tabs[i+1]);
16721 showPanelPrev : function()
16723 var i = this.indexOfPanel(this.getActivePanel());
16725 if (i < 1 && !this.autoslide) {
16729 if (i < 1 && this.autoslide) {
16730 i = this.tabs.length;
16733 this.showPanel(this.tabs[i-1]);
16737 addBullet: function()
16739 if(!this.bullets || Roo.isTouch){
16742 var ctr = this.el.select('.carousel-bullets',true).first();
16743 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16744 var bullet = ctr.createChild({
16745 cls : 'bullet bullet-' + i
16746 },ctr.dom.lastChild);
16751 bullet.on('click', (function(e, el, o, ii, t){
16753 e.preventDefault();
16755 this.showPanel(ii);
16757 if(this.autoslide && this.slideFn){
16758 clearInterval(this.slideFn);
16759 this.slideFn = window.setInterval(function() {
16760 _this.showPanelNext();
16764 }).createDelegate(this, [i, bullet], true));
16769 setActiveBullet : function(i)
16775 Roo.each(this.el.select('.bullet', true).elements, function(el){
16776 el.removeClass('selected');
16779 var bullet = this.el.select('.bullet-' + i, true).first();
16785 bullet.addClass('selected');
16796 Roo.apply(Roo.bootstrap.TabGroup, {
16800 * register a Navigation Group
16801 * @param {Roo.bootstrap.NavGroup} the navgroup to add
16803 register : function(navgrp)
16805 this.groups[navgrp.navId] = navgrp;
16809 * fetch a Navigation Group based on the navigation ID
16810 * if one does not exist , it will get created.
16811 * @param {string} the navgroup to add
16812 * @returns {Roo.bootstrap.NavGroup} the navgroup
16814 get: function(navId) {
16815 if (typeof(this.groups[navId]) == 'undefined') {
16816 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16818 return this.groups[navId] ;
16833 * @class Roo.bootstrap.TabPanel
16834 * @extends Roo.bootstrap.Component
16835 * Bootstrap TabPanel class
16836 * @cfg {Boolean} active panel active
16837 * @cfg {String} html panel content
16838 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16839 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16843 * Create a new TabPanel
16844 * @param {Object} config The config object
16847 Roo.bootstrap.TabPanel = function(config){
16848 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16852 * Fires when the active status changes
16853 * @param {Roo.bootstrap.TabPanel} this
16854 * @param {Boolean} state the new state
16859 * @event beforedeactivate
16860 * Fires before a tab is de-activated - can be used to do validation on a form.
16861 * @param {Roo.bootstrap.TabPanel} this
16862 * @return {Boolean} false if there is an error
16865 'beforedeactivate': true
16868 this.tabId = this.tabId || Roo.id();
16872 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
16879 getAutoCreate : function(){
16882 // item is needed for carousel - not sure if it has any effect otherwise
16883 cls: 'tab-pane item',
16884 html: this.html || ''
16888 cfg.cls += ' active';
16892 cfg.tabId = this.tabId;
16899 initEvents: function()
16901 var p = this.parent();
16902 this.navId = this.navId || p.navId;
16904 if (typeof(this.navId) != 'undefined') {
16905 // not really needed.. but just in case.. parent should be a NavGroup.
16906 var tg = Roo.bootstrap.TabGroup.get(this.navId);
16910 var i = tg.tabs.length - 1;
16912 if(this.active && tg.bullets > 0 && i < tg.bullets){
16913 tg.setActiveBullet(i);
16920 onRender : function(ct, position)
16922 // Roo.log("Call onRender: " + this.xtype);
16924 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16932 setActive: function(state)
16934 Roo.log("panel - set active " + this.tabId + "=" + state);
16936 this.active = state;
16938 this.el.removeClass('active');
16940 } else if (!this.el.hasClass('active')) {
16941 this.el.addClass('active');
16944 this.fireEvent('changed', this, state);
16961 * @class Roo.bootstrap.DateField
16962 * @extends Roo.bootstrap.Input
16963 * Bootstrap DateField class
16964 * @cfg {Number} weekStart default 0
16965 * @cfg {String} viewMode default empty, (months|years)
16966 * @cfg {String} minViewMode default empty, (months|years)
16967 * @cfg {Number} startDate default -Infinity
16968 * @cfg {Number} endDate default Infinity
16969 * @cfg {Boolean} todayHighlight default false
16970 * @cfg {Boolean} todayBtn default false
16971 * @cfg {Boolean} calendarWeeks default false
16972 * @cfg {Object} daysOfWeekDisabled default empty
16973 * @cfg {Boolean} singleMode default false (true | false)
16975 * @cfg {Boolean} keyboardNavigation default true
16976 * @cfg {String} language default en
16979 * Create a new DateField
16980 * @param {Object} config The config object
16983 Roo.bootstrap.DateField = function(config){
16984 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16988 * Fires when this field show.
16989 * @param {Roo.bootstrap.DateField} this
16990 * @param {Mixed} date The date value
16995 * Fires when this field hide.
16996 * @param {Roo.bootstrap.DateField} this
16997 * @param {Mixed} date The date value
17002 * Fires when select a date.
17003 * @param {Roo.bootstrap.DateField} this
17004 * @param {Mixed} date The date value
17008 * @event beforeselect
17009 * Fires when before select a date.
17010 * @param {Roo.bootstrap.DateField} this
17011 * @param {Mixed} date The date value
17013 beforeselect : true
17017 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17020 * @cfg {String} format
17021 * The default date format string which can be overriden for localization support. The format must be
17022 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17026 * @cfg {String} altFormats
17027 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17028 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17030 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17038 todayHighlight : false,
17044 keyboardNavigation: true,
17046 calendarWeeks: false,
17048 startDate: -Infinity,
17052 daysOfWeekDisabled: [],
17056 singleMode : false,
17058 UTCDate: function()
17060 return new Date(Date.UTC.apply(Date, arguments));
17063 UTCToday: function()
17065 var today = new Date();
17066 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17069 getDate: function() {
17070 var d = this.getUTCDate();
17071 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17074 getUTCDate: function() {
17078 setDate: function(d) {
17079 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17082 setUTCDate: function(d) {
17084 this.setValue(this.formatDate(this.date));
17087 onRender: function(ct, position)
17090 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17092 this.language = this.language || 'en';
17093 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17094 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17096 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17097 this.format = this.format || 'm/d/y';
17098 this.isInline = false;
17099 this.isInput = true;
17100 this.component = this.el.select('.add-on', true).first() || false;
17101 this.component = (this.component && this.component.length === 0) ? false : this.component;
17102 this.hasInput = this.component && this.inputEL().length;
17104 if (typeof(this.minViewMode === 'string')) {
17105 switch (this.minViewMode) {
17107 this.minViewMode = 1;
17110 this.minViewMode = 2;
17113 this.minViewMode = 0;
17118 if (typeof(this.viewMode === 'string')) {
17119 switch (this.viewMode) {
17132 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17134 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17136 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17138 this.picker().on('mousedown', this.onMousedown, this);
17139 this.picker().on('click', this.onClick, this);
17141 this.picker().addClass('datepicker-dropdown');
17143 this.startViewMode = this.viewMode;
17145 if(this.singleMode){
17146 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17147 v.setVisibilityMode(Roo.Element.DISPLAY);
17151 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17152 v.setStyle('width', '189px');
17156 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17157 if(!this.calendarWeeks){
17162 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17163 v.attr('colspan', function(i, val){
17164 return parseInt(val) + 1;
17169 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17171 this.setStartDate(this.startDate);
17172 this.setEndDate(this.endDate);
17174 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17181 if(this.isInline) {
17186 picker : function()
17188 return this.pickerEl;
17189 // return this.el.select('.datepicker', true).first();
17192 fillDow: function()
17194 var dowCnt = this.weekStart;
17203 if(this.calendarWeeks){
17211 while (dowCnt < this.weekStart + 7) {
17215 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17219 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17222 fillMonths: function()
17225 var months = this.picker().select('>.datepicker-months td', true).first();
17227 months.dom.innerHTML = '';
17233 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17236 months.createChild(month);
17243 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;
17245 if (this.date < this.startDate) {
17246 this.viewDate = new Date(this.startDate);
17247 } else if (this.date > this.endDate) {
17248 this.viewDate = new Date(this.endDate);
17250 this.viewDate = new Date(this.date);
17258 var d = new Date(this.viewDate),
17259 year = d.getUTCFullYear(),
17260 month = d.getUTCMonth(),
17261 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17262 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17263 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17264 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17265 currentDate = this.date && this.date.valueOf(),
17266 today = this.UTCToday();
17268 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17270 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17272 // this.picker.select('>tfoot th.today').
17273 // .text(dates[this.language].today)
17274 // .toggle(this.todayBtn !== false);
17276 this.updateNavArrows();
17279 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17281 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17283 prevMonth.setUTCDate(day);
17285 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17287 var nextMonth = new Date(prevMonth);
17289 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17291 nextMonth = nextMonth.valueOf();
17293 var fillMonths = false;
17295 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17297 while(prevMonth.valueOf() < nextMonth) {
17300 if (prevMonth.getUTCDay() === this.weekStart) {
17302 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17310 if(this.calendarWeeks){
17311 // ISO 8601: First week contains first thursday.
17312 // ISO also states week starts on Monday, but we can be more abstract here.
17314 // Start of current week: based on weekstart/current date
17315 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17316 // Thursday of this week
17317 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17318 // First Thursday of year, year from thursday
17319 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17320 // Calendar week: ms between thursdays, div ms per day, div 7 days
17321 calWeek = (th - yth) / 864e5 / 7 + 1;
17323 fillMonths.cn.push({
17331 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17333 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17336 if (this.todayHighlight &&
17337 prevMonth.getUTCFullYear() == today.getFullYear() &&
17338 prevMonth.getUTCMonth() == today.getMonth() &&
17339 prevMonth.getUTCDate() == today.getDate()) {
17340 clsName += ' today';
17343 if (currentDate && prevMonth.valueOf() === currentDate) {
17344 clsName += ' active';
17347 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17348 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17349 clsName += ' disabled';
17352 fillMonths.cn.push({
17354 cls: 'day ' + clsName,
17355 html: prevMonth.getDate()
17358 prevMonth.setDate(prevMonth.getDate()+1);
17361 var currentYear = this.date && this.date.getUTCFullYear();
17362 var currentMonth = this.date && this.date.getUTCMonth();
17364 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17366 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17367 v.removeClass('active');
17369 if(currentYear === year && k === currentMonth){
17370 v.addClass('active');
17373 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17374 v.addClass('disabled');
17380 year = parseInt(year/10, 10) * 10;
17382 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17384 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17387 for (var i = -1; i < 11; i++) {
17388 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17390 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17398 showMode: function(dir)
17401 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17404 Roo.each(this.picker().select('>div',true).elements, function(v){
17405 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17408 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17413 if(this.isInline) {
17417 this.picker().removeClass(['bottom', 'top']);
17419 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17421 * place to the top of element!
17425 this.picker().addClass('top');
17426 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17431 this.picker().addClass('bottom');
17433 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17436 parseDate : function(value)
17438 if(!value || value instanceof Date){
17441 var v = Date.parseDate(value, this.format);
17442 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17443 v = Date.parseDate(value, 'Y-m-d');
17445 if(!v && this.altFormats){
17446 if(!this.altFormatsArray){
17447 this.altFormatsArray = this.altFormats.split("|");
17449 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17450 v = Date.parseDate(value, this.altFormatsArray[i]);
17456 formatDate : function(date, fmt)
17458 return (!date || !(date instanceof Date)) ?
17459 date : date.dateFormat(fmt || this.format);
17462 onFocus : function()
17464 Roo.bootstrap.DateField.superclass.onFocus.call(this);
17468 onBlur : function()
17470 Roo.bootstrap.DateField.superclass.onBlur.call(this);
17472 var d = this.inputEl().getValue();
17481 this.picker().show();
17485 this.fireEvent('show', this, this.date);
17490 if(this.isInline) {
17493 this.picker().hide();
17494 this.viewMode = this.startViewMode;
17497 this.fireEvent('hide', this, this.date);
17501 onMousedown: function(e)
17503 e.stopPropagation();
17504 e.preventDefault();
17509 Roo.bootstrap.DateField.superclass.keyup.call(this);
17513 setValue: function(v)
17515 if(this.fireEvent('beforeselect', this, v) !== false){
17516 var d = new Date(this.parseDate(v) ).clearTime();
17518 if(isNaN(d.getTime())){
17519 this.date = this.viewDate = '';
17520 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17524 v = this.formatDate(d);
17526 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17528 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17532 this.fireEvent('select', this, this.date);
17536 getValue: function()
17538 return this.formatDate(this.date);
17541 fireKey: function(e)
17543 if (!this.picker().isVisible()){
17544 if (e.keyCode == 27) { // allow escape to hide and re-show picker
17550 var dateChanged = false,
17552 newDate, newViewDate;
17557 e.preventDefault();
17561 if (!this.keyboardNavigation) {
17564 dir = e.keyCode == 37 ? -1 : 1;
17567 newDate = this.moveYear(this.date, dir);
17568 newViewDate = this.moveYear(this.viewDate, dir);
17569 } else if (e.shiftKey){
17570 newDate = this.moveMonth(this.date, dir);
17571 newViewDate = this.moveMonth(this.viewDate, dir);
17573 newDate = new Date(this.date);
17574 newDate.setUTCDate(this.date.getUTCDate() + dir);
17575 newViewDate = new Date(this.viewDate);
17576 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17578 if (this.dateWithinRange(newDate)){
17579 this.date = newDate;
17580 this.viewDate = newViewDate;
17581 this.setValue(this.formatDate(this.date));
17583 e.preventDefault();
17584 dateChanged = true;
17589 if (!this.keyboardNavigation) {
17592 dir = e.keyCode == 38 ? -1 : 1;
17594 newDate = this.moveYear(this.date, dir);
17595 newViewDate = this.moveYear(this.viewDate, dir);
17596 } else if (e.shiftKey){
17597 newDate = this.moveMonth(this.date, dir);
17598 newViewDate = this.moveMonth(this.viewDate, dir);
17600 newDate = new Date(this.date);
17601 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17602 newViewDate = new Date(this.viewDate);
17603 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17605 if (this.dateWithinRange(newDate)){
17606 this.date = newDate;
17607 this.viewDate = newViewDate;
17608 this.setValue(this.formatDate(this.date));
17610 e.preventDefault();
17611 dateChanged = true;
17615 this.setValue(this.formatDate(this.date));
17617 e.preventDefault();
17620 this.setValue(this.formatDate(this.date));
17634 onClick: function(e)
17636 e.stopPropagation();
17637 e.preventDefault();
17639 var target = e.getTarget();
17641 if(target.nodeName.toLowerCase() === 'i'){
17642 target = Roo.get(target).dom.parentNode;
17645 var nodeName = target.nodeName;
17646 var className = target.className;
17647 var html = target.innerHTML;
17648 //Roo.log(nodeName);
17650 switch(nodeName.toLowerCase()) {
17652 switch(className) {
17658 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17659 switch(this.viewMode){
17661 this.viewDate = this.moveMonth(this.viewDate, dir);
17665 this.viewDate = this.moveYear(this.viewDate, dir);
17671 var date = new Date();
17672 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17674 this.setValue(this.formatDate(this.date));
17681 if (className.indexOf('disabled') < 0) {
17682 this.viewDate.setUTCDate(1);
17683 if (className.indexOf('month') > -1) {
17684 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17686 var year = parseInt(html, 10) || 0;
17687 this.viewDate.setUTCFullYear(year);
17691 if(this.singleMode){
17692 this.setValue(this.formatDate(this.viewDate));
17703 //Roo.log(className);
17704 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17705 var day = parseInt(html, 10) || 1;
17706 var year = this.viewDate.getUTCFullYear(),
17707 month = this.viewDate.getUTCMonth();
17709 if (className.indexOf('old') > -1) {
17716 } else if (className.indexOf('new') > -1) {
17724 //Roo.log([year,month,day]);
17725 this.date = this.UTCDate(year, month, day,0,0,0,0);
17726 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17728 //Roo.log(this.formatDate(this.date));
17729 this.setValue(this.formatDate(this.date));
17736 setStartDate: function(startDate)
17738 this.startDate = startDate || -Infinity;
17739 if (this.startDate !== -Infinity) {
17740 this.startDate = this.parseDate(this.startDate);
17743 this.updateNavArrows();
17746 setEndDate: function(endDate)
17748 this.endDate = endDate || Infinity;
17749 if (this.endDate !== Infinity) {
17750 this.endDate = this.parseDate(this.endDate);
17753 this.updateNavArrows();
17756 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17758 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17759 if (typeof(this.daysOfWeekDisabled) !== 'object') {
17760 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17762 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17763 return parseInt(d, 10);
17766 this.updateNavArrows();
17769 updateNavArrows: function()
17771 if(this.singleMode){
17775 var d = new Date(this.viewDate),
17776 year = d.getUTCFullYear(),
17777 month = d.getUTCMonth();
17779 Roo.each(this.picker().select('.prev', true).elements, function(v){
17781 switch (this.viewMode) {
17784 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17790 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17797 Roo.each(this.picker().select('.next', true).elements, function(v){
17799 switch (this.viewMode) {
17802 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17808 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17816 moveMonth: function(date, dir)
17821 var new_date = new Date(date.valueOf()),
17822 day = new_date.getUTCDate(),
17823 month = new_date.getUTCMonth(),
17824 mag = Math.abs(dir),
17826 dir = dir > 0 ? 1 : -1;
17829 // If going back one month, make sure month is not current month
17830 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17832 return new_date.getUTCMonth() == month;
17834 // If going forward one month, make sure month is as expected
17835 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17837 return new_date.getUTCMonth() != new_month;
17839 new_month = month + dir;
17840 new_date.setUTCMonth(new_month);
17841 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17842 if (new_month < 0 || new_month > 11) {
17843 new_month = (new_month + 12) % 12;
17846 // For magnitudes >1, move one month at a time...
17847 for (var i=0; i<mag; i++) {
17848 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17849 new_date = this.moveMonth(new_date, dir);
17851 // ...then reset the day, keeping it in the new month
17852 new_month = new_date.getUTCMonth();
17853 new_date.setUTCDate(day);
17855 return new_month != new_date.getUTCMonth();
17858 // Common date-resetting loop -- if date is beyond end of month, make it
17861 new_date.setUTCDate(--day);
17862 new_date.setUTCMonth(new_month);
17867 moveYear: function(date, dir)
17869 return this.moveMonth(date, dir*12);
17872 dateWithinRange: function(date)
17874 return date >= this.startDate && date <= this.endDate;
17880 this.picker().remove();
17885 Roo.apply(Roo.bootstrap.DateField, {
17896 html: '<i class="fa fa-arrow-left"/>'
17906 html: '<i class="fa fa-arrow-right"/>'
17948 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17949 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17950 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17951 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17952 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17965 navFnc: 'FullYear',
17970 navFnc: 'FullYear',
17975 Roo.apply(Roo.bootstrap.DateField, {
17979 cls: 'datepicker dropdown-menu roo-dynamic',
17983 cls: 'datepicker-days',
17987 cls: 'table-condensed',
17989 Roo.bootstrap.DateField.head,
17993 Roo.bootstrap.DateField.footer
18000 cls: 'datepicker-months',
18004 cls: 'table-condensed',
18006 Roo.bootstrap.DateField.head,
18007 Roo.bootstrap.DateField.content,
18008 Roo.bootstrap.DateField.footer
18015 cls: 'datepicker-years',
18019 cls: 'table-condensed',
18021 Roo.bootstrap.DateField.head,
18022 Roo.bootstrap.DateField.content,
18023 Roo.bootstrap.DateField.footer
18042 * @class Roo.bootstrap.TimeField
18043 * @extends Roo.bootstrap.Input
18044 * Bootstrap DateField class
18048 * Create a new TimeField
18049 * @param {Object} config The config object
18052 Roo.bootstrap.TimeField = function(config){
18053 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18057 * Fires when this field show.
18058 * @param {Roo.bootstrap.DateField} thisthis
18059 * @param {Mixed} date The date value
18064 * Fires when this field hide.
18065 * @param {Roo.bootstrap.DateField} this
18066 * @param {Mixed} date The date value
18071 * Fires when select a date.
18072 * @param {Roo.bootstrap.DateField} this
18073 * @param {Mixed} date The date value
18079 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18082 * @cfg {String} format
18083 * The default time format string which can be overriden for localization support. The format must be
18084 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18088 onRender: function(ct, position)
18091 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18093 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18095 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18097 this.pop = this.picker().select('>.datepicker-time',true).first();
18098 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18100 this.picker().on('mousedown', this.onMousedown, this);
18101 this.picker().on('click', this.onClick, this);
18103 this.picker().addClass('datepicker-dropdown');
18108 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18109 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18110 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18111 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18112 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18113 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18117 fireKey: function(e){
18118 if (!this.picker().isVisible()){
18119 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18125 e.preventDefault();
18133 this.onTogglePeriod();
18136 this.onIncrementMinutes();
18139 this.onDecrementMinutes();
18148 onClick: function(e) {
18149 e.stopPropagation();
18150 e.preventDefault();
18153 picker : function()
18155 return this.el.select('.datepicker', true).first();
18158 fillTime: function()
18160 var time = this.pop.select('tbody', true).first();
18162 time.dom.innerHTML = '';
18177 cls: 'hours-up glyphicon glyphicon-chevron-up'
18197 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18218 cls: 'timepicker-hour',
18233 cls: 'timepicker-minute',
18248 cls: 'btn btn-primary period',
18270 cls: 'hours-down glyphicon glyphicon-chevron-down'
18290 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18308 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18315 var hours = this.time.getHours();
18316 var minutes = this.time.getMinutes();
18329 hours = hours - 12;
18333 hours = '0' + hours;
18337 minutes = '0' + minutes;
18340 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18341 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18342 this.pop.select('button', true).first().dom.innerHTML = period;
18348 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18350 var cls = ['bottom'];
18352 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18359 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18364 this.picker().addClass(cls.join('-'));
18368 Roo.each(cls, function(c){
18370 _this.picker().setTop(_this.inputEl().getHeight());
18374 _this.picker().setTop(0 - _this.picker().getHeight());
18379 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18383 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18390 onFocus : function()
18392 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18396 onBlur : function()
18398 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18404 this.picker().show();
18409 this.fireEvent('show', this, this.date);
18414 this.picker().hide();
18417 this.fireEvent('hide', this, this.date);
18420 setTime : function()
18423 this.setValue(this.time.format(this.format));
18425 this.fireEvent('select', this, this.date);
18430 onMousedown: function(e){
18431 e.stopPropagation();
18432 e.preventDefault();
18435 onIncrementHours: function()
18437 Roo.log('onIncrementHours');
18438 this.time = this.time.add(Date.HOUR, 1);
18443 onDecrementHours: function()
18445 Roo.log('onDecrementHours');
18446 this.time = this.time.add(Date.HOUR, -1);
18450 onIncrementMinutes: function()
18452 Roo.log('onIncrementMinutes');
18453 this.time = this.time.add(Date.MINUTE, 1);
18457 onDecrementMinutes: function()
18459 Roo.log('onDecrementMinutes');
18460 this.time = this.time.add(Date.MINUTE, -1);
18464 onTogglePeriod: function()
18466 Roo.log('onTogglePeriod');
18467 this.time = this.time.add(Date.HOUR, 12);
18474 Roo.apply(Roo.bootstrap.TimeField, {
18504 cls: 'btn btn-info ok',
18516 Roo.apply(Roo.bootstrap.TimeField, {
18520 cls: 'datepicker dropdown-menu',
18524 cls: 'datepicker-time',
18528 cls: 'table-condensed',
18530 Roo.bootstrap.TimeField.content,
18531 Roo.bootstrap.TimeField.footer
18550 * @class Roo.bootstrap.MonthField
18551 * @extends Roo.bootstrap.Input
18552 * Bootstrap MonthField class
18554 * @cfg {String} language default en
18557 * Create a new MonthField
18558 * @param {Object} config The config object
18561 Roo.bootstrap.MonthField = function(config){
18562 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18567 * Fires when this field show.
18568 * @param {Roo.bootstrap.MonthField} this
18569 * @param {Mixed} date The date value
18574 * Fires when this field hide.
18575 * @param {Roo.bootstrap.MonthField} this
18576 * @param {Mixed} date The date value
18581 * Fires when select a date.
18582 * @param {Roo.bootstrap.MonthField} this
18583 * @param {String} oldvalue The old value
18584 * @param {String} newvalue The new value
18590 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
18592 onRender: function(ct, position)
18595 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18597 this.language = this.language || 'en';
18598 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18599 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18601 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18602 this.isInline = false;
18603 this.isInput = true;
18604 this.component = this.el.select('.add-on', true).first() || false;
18605 this.component = (this.component && this.component.length === 0) ? false : this.component;
18606 this.hasInput = this.component && this.inputEL().length;
18608 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18610 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18612 this.picker().on('mousedown', this.onMousedown, this);
18613 this.picker().on('click', this.onClick, this);
18615 this.picker().addClass('datepicker-dropdown');
18617 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18618 v.setStyle('width', '189px');
18625 if(this.isInline) {
18631 setValue: function(v, suppressEvent)
18633 var o = this.getValue();
18635 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18639 if(suppressEvent !== true){
18640 this.fireEvent('select', this, o, v);
18645 getValue: function()
18650 onClick: function(e)
18652 e.stopPropagation();
18653 e.preventDefault();
18655 var target = e.getTarget();
18657 if(target.nodeName.toLowerCase() === 'i'){
18658 target = Roo.get(target).dom.parentNode;
18661 var nodeName = target.nodeName;
18662 var className = target.className;
18663 var html = target.innerHTML;
18665 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18669 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18671 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18677 picker : function()
18679 return this.pickerEl;
18682 fillMonths: function()
18685 var months = this.picker().select('>.datepicker-months td', true).first();
18687 months.dom.innerHTML = '';
18693 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18696 months.createChild(month);
18705 if(typeof(this.vIndex) == 'undefined' && this.value.length){
18706 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18709 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18710 e.removeClass('active');
18712 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18713 e.addClass('active');
18720 if(this.isInline) {
18724 this.picker().removeClass(['bottom', 'top']);
18726 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18728 * place to the top of element!
18732 this.picker().addClass('top');
18733 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18738 this.picker().addClass('bottom');
18740 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18743 onFocus : function()
18745 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18749 onBlur : function()
18751 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18753 var d = this.inputEl().getValue();
18762 this.picker().show();
18763 this.picker().select('>.datepicker-months', true).first().show();
18767 this.fireEvent('show', this, this.date);
18772 if(this.isInline) {
18775 this.picker().hide();
18776 this.fireEvent('hide', this, this.date);
18780 onMousedown: function(e)
18782 e.stopPropagation();
18783 e.preventDefault();
18788 Roo.bootstrap.MonthField.superclass.keyup.call(this);
18792 fireKey: function(e)
18794 if (!this.picker().isVisible()){
18795 if (e.keyCode == 27) {// allow escape to hide and re-show picker
18806 e.preventDefault();
18810 dir = e.keyCode == 37 ? -1 : 1;
18812 this.vIndex = this.vIndex + dir;
18814 if(this.vIndex < 0){
18818 if(this.vIndex > 11){
18822 if(isNaN(this.vIndex)){
18826 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18832 dir = e.keyCode == 38 ? -1 : 1;
18834 this.vIndex = this.vIndex + dir * 4;
18836 if(this.vIndex < 0){
18840 if(this.vIndex > 11){
18844 if(isNaN(this.vIndex)){
18848 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18853 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18854 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18858 e.preventDefault();
18861 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18862 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18878 this.picker().remove();
18883 Roo.apply(Roo.bootstrap.MonthField, {
18902 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18903 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18908 Roo.apply(Roo.bootstrap.MonthField, {
18912 cls: 'datepicker dropdown-menu roo-dynamic',
18916 cls: 'datepicker-months',
18920 cls: 'table-condensed',
18922 Roo.bootstrap.DateField.content
18942 * @class Roo.bootstrap.CheckBox
18943 * @extends Roo.bootstrap.Input
18944 * Bootstrap CheckBox class
18946 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18947 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18948 * @cfg {String} boxLabel The text that appears beside the checkbox
18949 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18950 * @cfg {Boolean} checked initnal the element
18951 * @cfg {Boolean} inline inline the element (default false)
18952 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18955 * Create a new CheckBox
18956 * @param {Object} config The config object
18959 Roo.bootstrap.CheckBox = function(config){
18960 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18965 * Fires when the element is checked or unchecked.
18966 * @param {Roo.bootstrap.CheckBox} this This input
18967 * @param {Boolean} checked The new checked value
18974 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
18976 inputType: 'checkbox',
18984 getAutoCreate : function()
18986 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18992 cfg.cls = 'form-group ' + this.inputType; //input-group
18995 cfg.cls += ' ' + this.inputType + '-inline';
19001 type : this.inputType,
19002 value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
19003 cls : 'roo-' + this.inputType, //'form-box',
19004 placeholder : this.placeholder || ''
19008 if (this.weight) { // Validity check?
19009 cfg.cls += " " + this.inputType + "-" + this.weight;
19012 if (this.disabled) {
19013 input.disabled=true;
19017 input.checked = this.checked;
19021 input.name = this.name;
19025 input.cls += ' input-' + this.size;
19030 ['xs','sm','md','lg'].map(function(size){
19031 if (settings[size]) {
19032 cfg.cls += ' col-' + size + '-' + settings[size];
19036 var inputblock = input;
19038 if (this.before || this.after) {
19041 cls : 'input-group',
19046 inputblock.cn.push({
19048 cls : 'input-group-addon',
19053 inputblock.cn.push(input);
19056 inputblock.cn.push({
19058 cls : 'input-group-addon',
19065 if (align ==='left' && this.fieldLabel.length) {
19066 // Roo.log("left and has label");
19072 cls : 'control-label col-md-' + this.labelWidth,
19073 html : this.fieldLabel
19077 cls : "col-md-" + (12 - this.labelWidth),
19084 } else if ( this.fieldLabel.length) {
19085 // Roo.log(" label");
19089 tag: this.boxLabel ? 'span' : 'label',
19091 cls: 'control-label box-input-label',
19092 //cls : 'input-group-addon',
19093 html : this.fieldLabel
19103 // Roo.log(" no label && no align");
19104 cfg.cn = [ inputblock ] ;
19110 var boxLabelCfg = {
19112 //'for': id, // box label is handled by onclick - so no for...
19114 html: this.boxLabel
19118 boxLabelCfg.tooltip = this.tooltip;
19121 cfg.cn.push(boxLabelCfg);
19131 * return the real input element.
19133 inputEl: function ()
19135 return this.el.select('input.roo-' + this.inputType,true).first();
19138 labelEl: function()
19140 return this.el.select('label.control-label',true).first();
19142 /* depricated... */
19146 return this.labelEl();
19149 boxLabelEl: function()
19151 return this.el.select('label.box-label',true).first();
19154 initEvents : function()
19156 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19158 this.inputEl().on('click', this.onClick, this);
19160 if (this.boxLabel) {
19161 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19164 this.startValue = this.getValue();
19167 Roo.bootstrap.CheckBox.register(this);
19171 onClick : function()
19173 this.setChecked(!this.checked);
19176 setChecked : function(state,suppressEvent)
19178 this.startValue = this.getValue();
19180 if(this.inputType == 'radio'){
19182 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19183 e.dom.checked = false;
19186 this.inputEl().dom.checked = true;
19188 this.inputEl().dom.value = this.inputValue;
19190 if(suppressEvent !== true){
19191 this.fireEvent('check', this, true);
19199 this.checked = state;
19201 this.inputEl().dom.checked = state;
19203 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19205 if(suppressEvent !== true){
19206 this.fireEvent('check', this, state);
19212 getValue : function()
19214 if(this.inputType == 'radio'){
19215 return this.getGroupValue();
19218 return this.inputEl().getValue();
19222 getGroupValue : function()
19224 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19228 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19231 setValue : function(v,suppressEvent)
19233 if(this.inputType == 'radio'){
19234 this.setGroupValue(v, suppressEvent);
19238 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19243 setGroupValue : function(v, suppressEvent)
19245 this.startValue = this.getValue();
19247 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19248 e.dom.checked = false;
19250 if(e.dom.value == v){
19251 e.dom.checked = true;
19255 if(suppressEvent !== true){
19256 this.fireEvent('check', this, true);
19264 validate : function()
19268 (this.inputType == 'radio' && this.validateRadio()) ||
19269 (this.inputType == 'checkbox' && this.validateCheckbox())
19275 this.markInvalid();
19279 validateRadio : function()
19283 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19284 if(!e.dom.checked){
19296 validateCheckbox : function()
19299 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19302 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19310 for(var i in group){
19315 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19322 * Mark this field as valid
19324 markValid : function()
19326 if(this.allowBlank){
19332 this.fireEvent('valid', this);
19334 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19337 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19344 if(this.inputType == 'radio'){
19345 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19346 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19347 e.findParent('.form-group', false, true).addClass(_this.validClass);
19354 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19355 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19359 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19365 for(var i in group){
19366 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19367 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19372 * Mark this field as invalid
19373 * @param {String} msg The validation message
19375 markInvalid : function(msg)
19377 if(this.allowBlank){
19383 this.fireEvent('invalid', this, msg);
19385 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19388 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19392 label.markInvalid();
19395 if(this.inputType == 'radio'){
19396 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19397 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19398 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19405 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19406 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19410 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19416 for(var i in group){
19417 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19418 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19425 Roo.apply(Roo.bootstrap.CheckBox, {
19430 * register a CheckBox Group
19431 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19433 register : function(checkbox)
19435 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19436 this.groups[checkbox.groupId] = {};
19439 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19443 this.groups[checkbox.groupId][checkbox.name] = checkbox;
19447 * fetch a CheckBox Group based on the group ID
19448 * @param {string} the group ID
19449 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19451 get: function(groupId) {
19452 if (typeof(this.groups[groupId]) == 'undefined') {
19456 return this.groups[groupId] ;
19468 *<div class="radio">
19470 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19471 Option one is this and that—be sure to include why it's great
19478 *<label class="radio-inline">fieldLabel</label>
19479 *<label class="radio-inline">
19480 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19488 * @class Roo.bootstrap.Radio
19489 * @extends Roo.bootstrap.CheckBox
19490 * Bootstrap Radio class
19493 * Create a new Radio
19494 * @param {Object} config The config object
19497 Roo.bootstrap.Radio = function(config){
19498 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19502 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
19504 inputType: 'radio',
19508 getAutoCreate : function()
19510 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19511 align = align || 'left'; // default...
19518 tag : this.inline ? 'span' : 'div',
19523 var inline = this.inline ? ' radio-inline' : '';
19527 // does not need for, as we wrap the input with it..
19529 cls : 'control-label box-label' + inline,
19532 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19536 //cls : 'control-label' + inline,
19537 html : this.fieldLabel,
19538 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19547 type : this.inputType,
19548 //value : (!this.checked) ? this.valueOff : this.inputValue,
19549 value : this.inputValue,
19551 placeholder : this.placeholder || '' // ?? needed????
19554 if (this.weight) { // Validity check?
19555 input.cls += " radio-" + this.weight;
19557 if (this.disabled) {
19558 input.disabled=true;
19562 input.checked = this.checked;
19566 input.name = this.name;
19570 input.cls += ' input-' + this.size;
19573 //?? can span's inline have a width??
19576 ['xs','sm','md','lg'].map(function(size){
19577 if (settings[size]) {
19578 cfg.cls += ' col-' + size + '-' + settings[size];
19582 var inputblock = input;
19584 if (this.before || this.after) {
19587 cls : 'input-group',
19592 inputblock.cn.push({
19594 cls : 'input-group-addon',
19598 inputblock.cn.push(input);
19600 inputblock.cn.push({
19602 cls : 'input-group-addon',
19610 if (this.fieldLabel && this.fieldLabel.length) {
19611 cfg.cn.push(fieldLabel);
19614 // normal bootstrap puts the input inside the label.
19615 // however with our styled version - it has to go after the input.
19617 //lbl.cn.push(inputblock);
19621 cls: 'radio' + inline,
19628 cfg.cn.push( lblwrap);
19633 html: this.boxLabel
19642 initEvents : function()
19644 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19646 this.inputEl().on('click', this.onClick, this);
19647 if (this.boxLabel) {
19648 //Roo.log('find label');
19649 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
19654 inputEl: function ()
19656 return this.el.select('input.roo-radio',true).first();
19658 onClick : function()
19661 this.setChecked(true);
19664 setChecked : function(state,suppressEvent)
19667 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19668 v.dom.checked = false;
19671 Roo.log(this.inputEl().dom);
19672 this.checked = state;
19673 this.inputEl().dom.checked = state;
19675 if(suppressEvent !== true){
19676 this.fireEvent('check', this, state);
19679 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19683 getGroupValue : function()
19686 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19687 if(v.dom.checked == true){
19688 value = v.dom.value;
19696 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
19697 * @return {Mixed} value The field value
19699 getValue : function(){
19700 return this.getGroupValue();
19706 //<script type="text/javascript">
19709 * Based Ext JS Library 1.1.1
19710 * Copyright(c) 2006-2007, Ext JS, LLC.
19716 * @class Roo.HtmlEditorCore
19717 * @extends Roo.Component
19718 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19720 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19723 Roo.HtmlEditorCore = function(config){
19726 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19731 * @event initialize
19732 * Fires when the editor is fully initialized (including the iframe)
19733 * @param {Roo.HtmlEditorCore} this
19738 * Fires when the editor is first receives the focus. Any insertion must wait
19739 * until after this event.
19740 * @param {Roo.HtmlEditorCore} this
19744 * @event beforesync
19745 * Fires before the textarea is updated with content from the editor iframe. Return false
19746 * to cancel the sync.
19747 * @param {Roo.HtmlEditorCore} this
19748 * @param {String} html
19752 * @event beforepush
19753 * Fires before the iframe editor is updated with content from the textarea. Return false
19754 * to cancel the push.
19755 * @param {Roo.HtmlEditorCore} this
19756 * @param {String} html
19761 * Fires when the textarea is updated with content from the editor iframe.
19762 * @param {Roo.HtmlEditorCore} this
19763 * @param {String} html
19768 * Fires when the iframe editor is updated with content from the textarea.
19769 * @param {Roo.HtmlEditorCore} this
19770 * @param {String} html
19775 * @event editorevent
19776 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19777 * @param {Roo.HtmlEditorCore} this
19783 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19785 // defaults : white / black...
19786 this.applyBlacklists();
19793 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
19797 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
19803 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
19808 * @cfg {Number} height (in pixels)
19812 * @cfg {Number} width (in pixels)
19817 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19820 stylesheets: false,
19825 // private properties
19826 validationEvent : false,
19828 initialized : false,
19830 sourceEditMode : false,
19831 onFocus : Roo.emptyFn,
19833 hideMode:'offsets',
19837 // blacklist + whitelisted elements..
19844 * Protected method that will not generally be called directly. It
19845 * is called when the editor initializes the iframe with HTML contents. Override this method if you
19846 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19848 getDocMarkup : function(){
19852 // inherit styels from page...??
19853 if (this.stylesheets === false) {
19855 Roo.get(document.head).select('style').each(function(node) {
19856 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19859 Roo.get(document.head).select('link').each(function(node) {
19860 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19863 } else if (!this.stylesheets.length) {
19865 st = '<style type="text/css">' +
19866 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19872 st += '<style type="text/css">' +
19873 'IMG { cursor: pointer } ' +
19877 return '<html><head>' + st +
19878 //<style type="text/css">' +
19879 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19881 ' </head><body class="roo-htmleditor-body"></body></html>';
19885 onRender : function(ct, position)
19888 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19889 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19892 this.el.dom.style.border = '0 none';
19893 this.el.dom.setAttribute('tabIndex', -1);
19894 this.el.addClass('x-hidden hide');
19898 if(Roo.isIE){ // fix IE 1px bogus margin
19899 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19903 this.frameId = Roo.id();
19907 var iframe = this.owner.wrap.createChild({
19909 cls: 'form-control', // bootstrap..
19911 name: this.frameId,
19912 frameBorder : 'no',
19913 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
19918 this.iframe = iframe.dom;
19920 this.assignDocWin();
19922 this.doc.designMode = 'on';
19925 this.doc.write(this.getDocMarkup());
19929 var task = { // must defer to wait for browser to be ready
19931 //console.log("run task?" + this.doc.readyState);
19932 this.assignDocWin();
19933 if(this.doc.body || this.doc.readyState == 'complete'){
19935 this.doc.designMode="on";
19939 Roo.TaskMgr.stop(task);
19940 this.initEditor.defer(10, this);
19947 Roo.TaskMgr.start(task);
19952 onResize : function(w, h)
19954 Roo.log('resize: ' +w + ',' + h );
19955 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19959 if(typeof w == 'number'){
19961 this.iframe.style.width = w + 'px';
19963 if(typeof h == 'number'){
19965 this.iframe.style.height = h + 'px';
19967 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19974 * Toggles the editor between standard and source edit mode.
19975 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19977 toggleSourceEdit : function(sourceEditMode){
19979 this.sourceEditMode = sourceEditMode === true;
19981 if(this.sourceEditMode){
19983 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
19986 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19987 //this.iframe.className = '';
19990 //this.setSize(this.owner.wrap.getSize());
19991 //this.fireEvent('editmodechange', this, this.sourceEditMode);
19998 * Protected method that will not generally be called directly. If you need/want
19999 * custom HTML cleanup, this is the method you should override.
20000 * @param {String} html The HTML to be cleaned
20001 * return {String} The cleaned HTML
20003 cleanHtml : function(html){
20004 html = String(html);
20005 if(html.length > 5){
20006 if(Roo.isSafari){ // strip safari nonsense
20007 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20010 if(html == ' '){
20017 * HTML Editor -> Textarea
20018 * Protected method that will not generally be called directly. Syncs the contents
20019 * of the editor iframe with the textarea.
20021 syncValue : function(){
20022 if(this.initialized){
20023 var bd = (this.doc.body || this.doc.documentElement);
20024 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20025 var html = bd.innerHTML;
20027 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20028 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20030 html = '<div style="'+m[0]+'">' + html + '</div>';
20033 html = this.cleanHtml(html);
20034 // fix up the special chars.. normaly like back quotes in word...
20035 // however we do not want to do this with chinese..
20036 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20037 var cc = b.charCodeAt();
20039 (cc >= 0x4E00 && cc < 0xA000 ) ||
20040 (cc >= 0x3400 && cc < 0x4E00 ) ||
20041 (cc >= 0xf900 && cc < 0xfb00 )
20047 if(this.owner.fireEvent('beforesync', this, html) !== false){
20048 this.el.dom.value = html;
20049 this.owner.fireEvent('sync', this, html);
20055 * Protected method that will not generally be called directly. Pushes the value of the textarea
20056 * into the iframe editor.
20058 pushValue : function(){
20059 if(this.initialized){
20060 var v = this.el.dom.value.trim();
20062 // if(v.length < 1){
20066 if(this.owner.fireEvent('beforepush', this, v) !== false){
20067 var d = (this.doc.body || this.doc.documentElement);
20069 this.cleanUpPaste();
20070 this.el.dom.value = d.innerHTML;
20071 this.owner.fireEvent('push', this, v);
20077 deferFocus : function(){
20078 this.focus.defer(10, this);
20082 focus : function(){
20083 if(this.win && !this.sourceEditMode){
20090 assignDocWin: function()
20092 var iframe = this.iframe;
20095 this.doc = iframe.contentWindow.document;
20096 this.win = iframe.contentWindow;
20098 // if (!Roo.get(this.frameId)) {
20101 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20102 // this.win = Roo.get(this.frameId).dom.contentWindow;
20104 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20108 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20109 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20114 initEditor : function(){
20115 //console.log("INIT EDITOR");
20116 this.assignDocWin();
20120 this.doc.designMode="on";
20122 this.doc.write(this.getDocMarkup());
20125 var dbody = (this.doc.body || this.doc.documentElement);
20126 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20127 // this copies styles from the containing element into thsi one..
20128 // not sure why we need all of this..
20129 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20131 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20132 //ss['background-attachment'] = 'fixed'; // w3c
20133 dbody.bgProperties = 'fixed'; // ie
20134 //Roo.DomHelper.applyStyles(dbody, ss);
20135 Roo.EventManager.on(this.doc, {
20136 //'mousedown': this.onEditorEvent,
20137 'mouseup': this.onEditorEvent,
20138 'dblclick': this.onEditorEvent,
20139 'click': this.onEditorEvent,
20140 'keyup': this.onEditorEvent,
20145 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20147 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20148 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20150 this.initialized = true;
20152 this.owner.fireEvent('initialize', this);
20157 onDestroy : function(){
20163 //for (var i =0; i < this.toolbars.length;i++) {
20164 // // fixme - ask toolbars for heights?
20165 // this.toolbars[i].onDestroy();
20168 //this.wrap.dom.innerHTML = '';
20169 //this.wrap.remove();
20174 onFirstFocus : function(){
20176 this.assignDocWin();
20179 this.activated = true;
20182 if(Roo.isGecko){ // prevent silly gecko errors
20184 var s = this.win.getSelection();
20185 if(!s.focusNode || s.focusNode.nodeType != 3){
20186 var r = s.getRangeAt(0);
20187 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20192 this.execCmd('useCSS', true);
20193 this.execCmd('styleWithCSS', false);
20196 this.owner.fireEvent('activate', this);
20200 adjustFont: function(btn){
20201 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20202 //if(Roo.isSafari){ // safari
20205 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20206 if(Roo.isSafari){ // safari
20207 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20208 v = (v < 10) ? 10 : v;
20209 v = (v > 48) ? 48 : v;
20210 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20215 v = Math.max(1, v+adjust);
20217 this.execCmd('FontSize', v );
20220 onEditorEvent : function(e)
20222 this.owner.fireEvent('editorevent', this, e);
20223 // this.updateToolbar();
20224 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20227 insertTag : function(tg)
20229 // could be a bit smarter... -> wrap the current selected tRoo..
20230 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20232 range = this.createRange(this.getSelection());
20233 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20234 wrappingNode.appendChild(range.extractContents());
20235 range.insertNode(wrappingNode);
20242 this.execCmd("formatblock", tg);
20246 insertText : function(txt)
20250 var range = this.createRange();
20251 range.deleteContents();
20252 //alert(Sender.getAttribute('label'));
20254 range.insertNode(this.doc.createTextNode(txt));
20260 * Executes a Midas editor command on the editor document and performs necessary focus and
20261 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20262 * @param {String} cmd The Midas command
20263 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20265 relayCmd : function(cmd, value){
20267 this.execCmd(cmd, value);
20268 this.owner.fireEvent('editorevent', this);
20269 //this.updateToolbar();
20270 this.owner.deferFocus();
20274 * Executes a Midas editor command directly on the editor document.
20275 * For visual commands, you should use {@link #relayCmd} instead.
20276 * <b>This should only be called after the editor is initialized.</b>
20277 * @param {String} cmd The Midas command
20278 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20280 execCmd : function(cmd, value){
20281 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20288 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20290 * @param {String} text | dom node..
20292 insertAtCursor : function(text)
20297 if(!this.activated){
20303 var r = this.doc.selection.createRange();
20314 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20318 // from jquery ui (MIT licenced)
20320 var win = this.win;
20322 if (win.getSelection && win.getSelection().getRangeAt) {
20323 range = win.getSelection().getRangeAt(0);
20324 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20325 range.insertNode(node);
20326 } else if (win.document.selection && win.document.selection.createRange) {
20327 // no firefox support
20328 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20329 win.document.selection.createRange().pasteHTML(txt);
20331 // no firefox support
20332 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20333 this.execCmd('InsertHTML', txt);
20342 mozKeyPress : function(e){
20344 var c = e.getCharCode(), cmd;
20347 c = String.fromCharCode(c).toLowerCase();
20361 this.cleanUpPaste.defer(100, this);
20369 e.preventDefault();
20377 fixKeys : function(){ // load time branching for fastest keydown performance
20379 return function(e){
20380 var k = e.getKey(), r;
20383 r = this.doc.selection.createRange();
20386 r.pasteHTML('    ');
20393 r = this.doc.selection.createRange();
20395 var target = r.parentElement();
20396 if(!target || target.tagName.toLowerCase() != 'li'){
20398 r.pasteHTML('<br />');
20404 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20405 this.cleanUpPaste.defer(100, this);
20411 }else if(Roo.isOpera){
20412 return function(e){
20413 var k = e.getKey();
20417 this.execCmd('InsertHTML','    ');
20420 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20421 this.cleanUpPaste.defer(100, this);
20426 }else if(Roo.isSafari){
20427 return function(e){
20428 var k = e.getKey();
20432 this.execCmd('InsertText','\t');
20436 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20437 this.cleanUpPaste.defer(100, this);
20445 getAllAncestors: function()
20447 var p = this.getSelectedNode();
20450 a.push(p); // push blank onto stack..
20451 p = this.getParentElement();
20455 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20459 a.push(this.doc.body);
20463 lastSelNode : false,
20466 getSelection : function()
20468 this.assignDocWin();
20469 return Roo.isIE ? this.doc.selection : this.win.getSelection();
20472 getSelectedNode: function()
20474 // this may only work on Gecko!!!
20476 // should we cache this!!!!
20481 var range = this.createRange(this.getSelection()).cloneRange();
20484 var parent = range.parentElement();
20486 var testRange = range.duplicate();
20487 testRange.moveToElementText(parent);
20488 if (testRange.inRange(range)) {
20491 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20494 parent = parent.parentElement;
20499 // is ancestor a text element.
20500 var ac = range.commonAncestorContainer;
20501 if (ac.nodeType == 3) {
20502 ac = ac.parentNode;
20505 var ar = ac.childNodes;
20508 var other_nodes = [];
20509 var has_other_nodes = false;
20510 for (var i=0;i<ar.length;i++) {
20511 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
20514 // fullly contained node.
20516 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20521 // probably selected..
20522 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20523 other_nodes.push(ar[i]);
20527 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
20532 has_other_nodes = true;
20534 if (!nodes.length && other_nodes.length) {
20535 nodes= other_nodes;
20537 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20543 createRange: function(sel)
20545 // this has strange effects when using with
20546 // top toolbar - not sure if it's a great idea.
20547 //this.editor.contentWindow.focus();
20548 if (typeof sel != "undefined") {
20550 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20552 return this.doc.createRange();
20555 return this.doc.createRange();
20558 getParentElement: function()
20561 this.assignDocWin();
20562 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20564 var range = this.createRange(sel);
20567 var p = range.commonAncestorContainer;
20568 while (p.nodeType == 3) { // text node
20579 * Range intersection.. the hard stuff...
20583 * [ -- selected range --- ]
20587 * if end is before start or hits it. fail.
20588 * if start is after end or hits it fail.
20590 * if either hits (but other is outside. - then it's not
20596 // @see http://www.thismuchiknow.co.uk/?p=64.
20597 rangeIntersectsNode : function(range, node)
20599 var nodeRange = node.ownerDocument.createRange();
20601 nodeRange.selectNode(node);
20603 nodeRange.selectNodeContents(node);
20606 var rangeStartRange = range.cloneRange();
20607 rangeStartRange.collapse(true);
20609 var rangeEndRange = range.cloneRange();
20610 rangeEndRange.collapse(false);
20612 var nodeStartRange = nodeRange.cloneRange();
20613 nodeStartRange.collapse(true);
20615 var nodeEndRange = nodeRange.cloneRange();
20616 nodeEndRange.collapse(false);
20618 return rangeStartRange.compareBoundaryPoints(
20619 Range.START_TO_START, nodeEndRange) == -1 &&
20620 rangeEndRange.compareBoundaryPoints(
20621 Range.START_TO_START, nodeStartRange) == 1;
20625 rangeCompareNode : function(range, node)
20627 var nodeRange = node.ownerDocument.createRange();
20629 nodeRange.selectNode(node);
20631 nodeRange.selectNodeContents(node);
20635 range.collapse(true);
20637 nodeRange.collapse(true);
20639 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20640 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
20642 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20644 var nodeIsBefore = ss == 1;
20645 var nodeIsAfter = ee == -1;
20647 if (nodeIsBefore && nodeIsAfter) {
20650 if (!nodeIsBefore && nodeIsAfter) {
20651 return 1; //right trailed.
20654 if (nodeIsBefore && !nodeIsAfter) {
20655 return 2; // left trailed.
20661 // private? - in a new class?
20662 cleanUpPaste : function()
20664 // cleans up the whole document..
20665 Roo.log('cleanuppaste');
20667 this.cleanUpChildren(this.doc.body);
20668 var clean = this.cleanWordChars(this.doc.body.innerHTML);
20669 if (clean != this.doc.body.innerHTML) {
20670 this.doc.body.innerHTML = clean;
20675 cleanWordChars : function(input) {// change the chars to hex code
20676 var he = Roo.HtmlEditorCore;
20678 var output = input;
20679 Roo.each(he.swapCodes, function(sw) {
20680 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20682 output = output.replace(swapper, sw[1]);
20689 cleanUpChildren : function (n)
20691 if (!n.childNodes.length) {
20694 for (var i = n.childNodes.length-1; i > -1 ; i--) {
20695 this.cleanUpChild(n.childNodes[i]);
20702 cleanUpChild : function (node)
20705 //console.log(node);
20706 if (node.nodeName == "#text") {
20707 // clean up silly Windows -- stuff?
20710 if (node.nodeName == "#comment") {
20711 node.parentNode.removeChild(node);
20712 // clean up silly Windows -- stuff?
20715 var lcname = node.tagName.toLowerCase();
20716 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20717 // whitelist of tags..
20719 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20721 node.parentNode.removeChild(node);
20726 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20728 // remove <a name=....> as rendering on yahoo mailer is borked with this.
20729 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20731 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20732 // remove_keep_children = true;
20735 if (remove_keep_children) {
20736 this.cleanUpChildren(node);
20737 // inserts everything just before this node...
20738 while (node.childNodes.length) {
20739 var cn = node.childNodes[0];
20740 node.removeChild(cn);
20741 node.parentNode.insertBefore(cn, node);
20743 node.parentNode.removeChild(node);
20747 if (!node.attributes || !node.attributes.length) {
20748 this.cleanUpChildren(node);
20752 function cleanAttr(n,v)
20755 if (v.match(/^\./) || v.match(/^\//)) {
20758 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20761 if (v.match(/^#/)) {
20764 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20765 node.removeAttribute(n);
20769 var cwhite = this.cwhite;
20770 var cblack = this.cblack;
20772 function cleanStyle(n,v)
20774 if (v.match(/expression/)) { //XSS?? should we even bother..
20775 node.removeAttribute(n);
20779 var parts = v.split(/;/);
20782 Roo.each(parts, function(p) {
20783 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20787 var l = p.split(':').shift().replace(/\s+/g,'');
20788 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20790 if ( cwhite.length && cblack.indexOf(l) > -1) {
20791 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20792 //node.removeAttribute(n);
20796 // only allow 'c whitelisted system attributes'
20797 if ( cwhite.length && cwhite.indexOf(l) < 0) {
20798 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20799 //node.removeAttribute(n);
20809 if (clean.length) {
20810 node.setAttribute(n, clean.join(';'));
20812 node.removeAttribute(n);
20818 for (var i = node.attributes.length-1; i > -1 ; i--) {
20819 var a = node.attributes[i];
20822 if (a.name.toLowerCase().substr(0,2)=='on') {
20823 node.removeAttribute(a.name);
20826 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20827 node.removeAttribute(a.name);
20830 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20831 cleanAttr(a.name,a.value); // fixme..
20834 if (a.name == 'style') {
20835 cleanStyle(a.name,a.value);
20838 /// clean up MS crap..
20839 // tecnically this should be a list of valid class'es..
20842 if (a.name == 'class') {
20843 if (a.value.match(/^Mso/)) {
20844 node.className = '';
20847 if (a.value.match(/body/)) {
20848 node.className = '';
20859 this.cleanUpChildren(node);
20865 * Clean up MS wordisms...
20867 cleanWord : function(node)
20872 this.cleanWord(this.doc.body);
20875 if (node.nodeName == "#text") {
20876 // clean up silly Windows -- stuff?
20879 if (node.nodeName == "#comment") {
20880 node.parentNode.removeChild(node);
20881 // clean up silly Windows -- stuff?
20885 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20886 node.parentNode.removeChild(node);
20890 // remove - but keep children..
20891 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20892 while (node.childNodes.length) {
20893 var cn = node.childNodes[0];
20894 node.removeChild(cn);
20895 node.parentNode.insertBefore(cn, node);
20897 node.parentNode.removeChild(node);
20898 this.iterateChildren(node, this.cleanWord);
20902 if (node.className.length) {
20904 var cn = node.className.split(/\W+/);
20906 Roo.each(cn, function(cls) {
20907 if (cls.match(/Mso[a-zA-Z]+/)) {
20912 node.className = cna.length ? cna.join(' ') : '';
20914 node.removeAttribute("class");
20918 if (node.hasAttribute("lang")) {
20919 node.removeAttribute("lang");
20922 if (node.hasAttribute("style")) {
20924 var styles = node.getAttribute("style").split(";");
20926 Roo.each(styles, function(s) {
20927 if (!s.match(/:/)) {
20930 var kv = s.split(":");
20931 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20934 // what ever is left... we allow.
20937 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20938 if (!nstyle.length) {
20939 node.removeAttribute('style');
20942 this.iterateChildren(node, this.cleanWord);
20948 * iterateChildren of a Node, calling fn each time, using this as the scole..
20949 * @param {DomNode} node node to iterate children of.
20950 * @param {Function} fn method of this class to call on each item.
20952 iterateChildren : function(node, fn)
20954 if (!node.childNodes.length) {
20957 for (var i = node.childNodes.length-1; i > -1 ; i--) {
20958 fn.call(this, node.childNodes[i])
20964 * cleanTableWidths.
20966 * Quite often pasting from word etc.. results in tables with column and widths.
20967 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20970 cleanTableWidths : function(node)
20975 this.cleanTableWidths(this.doc.body);
20980 if (node.nodeName == "#text" || node.nodeName == "#comment") {
20983 Roo.log(node.tagName);
20984 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20985 this.iterateChildren(node, this.cleanTableWidths);
20988 if (node.hasAttribute('width')) {
20989 node.removeAttribute('width');
20993 if (node.hasAttribute("style")) {
20996 var styles = node.getAttribute("style").split(";");
20998 Roo.each(styles, function(s) {
20999 if (!s.match(/:/)) {
21002 var kv = s.split(":");
21003 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21006 // what ever is left... we allow.
21009 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21010 if (!nstyle.length) {
21011 node.removeAttribute('style');
21015 this.iterateChildren(node, this.cleanTableWidths);
21023 domToHTML : function(currentElement, depth, nopadtext) {
21025 depth = depth || 0;
21026 nopadtext = nopadtext || false;
21028 if (!currentElement) {
21029 return this.domToHTML(this.doc.body);
21032 //Roo.log(currentElement);
21034 var allText = false;
21035 var nodeName = currentElement.nodeName;
21036 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21038 if (nodeName == '#text') {
21040 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21045 if (nodeName != 'BODY') {
21048 // Prints the node tagName, such as <A>, <IMG>, etc
21051 for(i = 0; i < currentElement.attributes.length;i++) {
21053 var aname = currentElement.attributes.item(i).name;
21054 if (!currentElement.attributes.item(i).value.length) {
21057 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21060 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21069 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21072 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21077 // Traverse the tree
21079 var currentElementChild = currentElement.childNodes.item(i);
21080 var allText = true;
21081 var innerHTML = '';
21083 while (currentElementChild) {
21084 // Formatting code (indent the tree so it looks nice on the screen)
21085 var nopad = nopadtext;
21086 if (lastnode == 'SPAN') {
21090 if (currentElementChild.nodeName == '#text') {
21091 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21092 toadd = nopadtext ? toadd : toadd.trim();
21093 if (!nopad && toadd.length > 80) {
21094 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21096 innerHTML += toadd;
21099 currentElementChild = currentElement.childNodes.item(i);
21105 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21107 // Recursively traverse the tree structure of the child node
21108 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21109 lastnode = currentElementChild.nodeName;
21111 currentElementChild=currentElement.childNodes.item(i);
21117 // The remaining code is mostly for formatting the tree
21118 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21123 ret+= "</"+tagName+">";
21129 applyBlacklists : function()
21131 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21132 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21136 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21137 if (b.indexOf(tag) > -1) {
21140 this.white.push(tag);
21144 Roo.each(w, function(tag) {
21145 if (b.indexOf(tag) > -1) {
21148 if (this.white.indexOf(tag) > -1) {
21151 this.white.push(tag);
21156 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21157 if (w.indexOf(tag) > -1) {
21160 this.black.push(tag);
21164 Roo.each(b, function(tag) {
21165 if (w.indexOf(tag) > -1) {
21168 if (this.black.indexOf(tag) > -1) {
21171 this.black.push(tag);
21176 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21177 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21181 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21182 if (b.indexOf(tag) > -1) {
21185 this.cwhite.push(tag);
21189 Roo.each(w, function(tag) {
21190 if (b.indexOf(tag) > -1) {
21193 if (this.cwhite.indexOf(tag) > -1) {
21196 this.cwhite.push(tag);
21201 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21202 if (w.indexOf(tag) > -1) {
21205 this.cblack.push(tag);
21209 Roo.each(b, function(tag) {
21210 if (w.indexOf(tag) > -1) {
21213 if (this.cblack.indexOf(tag) > -1) {
21216 this.cblack.push(tag);
21221 setStylesheets : function(stylesheets)
21223 if(typeof(stylesheets) == 'string'){
21224 Roo.get(this.iframe.contentDocument.head).createChild({
21226 rel : 'stylesheet',
21235 Roo.each(stylesheets, function(s) {
21240 Roo.get(_this.iframe.contentDocument.head).createChild({
21242 rel : 'stylesheet',
21251 removeStylesheets : function()
21255 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21260 // hide stuff that is not compatible
21274 * @event specialkey
21278 * @cfg {String} fieldClass @hide
21281 * @cfg {String} focusClass @hide
21284 * @cfg {String} autoCreate @hide
21287 * @cfg {String} inputType @hide
21290 * @cfg {String} invalidClass @hide
21293 * @cfg {String} invalidText @hide
21296 * @cfg {String} msgFx @hide
21299 * @cfg {String} validateOnBlur @hide
21303 Roo.HtmlEditorCore.white = [
21304 'area', 'br', 'img', 'input', 'hr', 'wbr',
21306 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21307 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21308 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21309 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21310 'table', 'ul', 'xmp',
21312 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21315 'dir', 'menu', 'ol', 'ul', 'dl',
21321 Roo.HtmlEditorCore.black = [
21322 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21324 'base', 'basefont', 'bgsound', 'blink', 'body',
21325 'frame', 'frameset', 'head', 'html', 'ilayer',
21326 'iframe', 'layer', 'link', 'meta', 'object',
21327 'script', 'style' ,'title', 'xml' // clean later..
21329 Roo.HtmlEditorCore.clean = [
21330 'script', 'style', 'title', 'xml'
21332 Roo.HtmlEditorCore.remove = [
21337 Roo.HtmlEditorCore.ablack = [
21341 Roo.HtmlEditorCore.aclean = [
21342 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21346 Roo.HtmlEditorCore.pwhite= [
21347 'http', 'https', 'mailto'
21350 // white listed style attributes.
21351 Roo.HtmlEditorCore.cwhite= [
21352 // 'text-align', /// default is to allow most things..
21358 // black listed style attributes.
21359 Roo.HtmlEditorCore.cblack= [
21360 // 'font-size' -- this can be set by the project
21364 Roo.HtmlEditorCore.swapCodes =[
21383 * @class Roo.bootstrap.HtmlEditor
21384 * @extends Roo.bootstrap.TextArea
21385 * Bootstrap HtmlEditor class
21388 * Create a new HtmlEditor
21389 * @param {Object} config The config object
21392 Roo.bootstrap.HtmlEditor = function(config){
21393 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21394 if (!this.toolbars) {
21395 this.toolbars = [];
21397 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21400 * @event initialize
21401 * Fires when the editor is fully initialized (including the iframe)
21402 * @param {HtmlEditor} this
21407 * Fires when the editor is first receives the focus. Any insertion must wait
21408 * until after this event.
21409 * @param {HtmlEditor} this
21413 * @event beforesync
21414 * Fires before the textarea is updated with content from the editor iframe. Return false
21415 * to cancel the sync.
21416 * @param {HtmlEditor} this
21417 * @param {String} html
21421 * @event beforepush
21422 * Fires before the iframe editor is updated with content from the textarea. Return false
21423 * to cancel the push.
21424 * @param {HtmlEditor} this
21425 * @param {String} html
21430 * Fires when the textarea is updated with content from the editor iframe.
21431 * @param {HtmlEditor} this
21432 * @param {String} html
21437 * Fires when the iframe editor is updated with content from the textarea.
21438 * @param {HtmlEditor} this
21439 * @param {String} html
21443 * @event editmodechange
21444 * Fires when the editor switches edit modes
21445 * @param {HtmlEditor} this
21446 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21448 editmodechange: true,
21450 * @event editorevent
21451 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21452 * @param {HtmlEditor} this
21456 * @event firstfocus
21457 * Fires when on first focus - needed by toolbars..
21458 * @param {HtmlEditor} this
21463 * Auto save the htmlEditor value as a file into Events
21464 * @param {HtmlEditor} this
21468 * @event savedpreview
21469 * preview the saved version of htmlEditor
21470 * @param {HtmlEditor} this
21477 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
21481 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21486 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21491 * @cfg {Number} height (in pixels)
21495 * @cfg {Number} width (in pixels)
21500 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21503 stylesheets: false,
21508 // private properties
21509 validationEvent : false,
21511 initialized : false,
21514 onFocus : Roo.emptyFn,
21516 hideMode:'offsets',
21519 tbContainer : false,
21521 toolbarContainer :function() {
21522 return this.wrap.select('.x-html-editor-tb',true).first();
21526 * Protected method that will not generally be called directly. It
21527 * is called when the editor creates its toolbar. Override this method if you need to
21528 * add custom toolbar buttons.
21529 * @param {HtmlEditor} editor
21531 createToolbar : function(){
21533 Roo.log("create toolbars");
21535 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21536 this.toolbars[0].render(this.toolbarContainer());
21540 // if (!editor.toolbars || !editor.toolbars.length) {
21541 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21544 // for (var i =0 ; i < editor.toolbars.length;i++) {
21545 // editor.toolbars[i] = Roo.factory(
21546 // typeof(editor.toolbars[i]) == 'string' ?
21547 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
21548 // Roo.bootstrap.HtmlEditor);
21549 // editor.toolbars[i].init(editor);
21555 onRender : function(ct, position)
21557 // Roo.log("Call onRender: " + this.xtype);
21559 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21561 this.wrap = this.inputEl().wrap({
21562 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21565 this.editorcore.onRender(ct, position);
21567 if (this.resizable) {
21568 this.resizeEl = new Roo.Resizable(this.wrap, {
21572 minHeight : this.height,
21573 height: this.height,
21574 handles : this.resizable,
21577 resize : function(r, w, h) {
21578 _t.onResize(w,h); // -something
21584 this.createToolbar(this);
21587 if(!this.width && this.resizable){
21588 this.setSize(this.wrap.getSize());
21590 if (this.resizeEl) {
21591 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21592 // should trigger onReize..
21598 onResize : function(w, h)
21600 Roo.log('resize: ' +w + ',' + h );
21601 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21605 if(this.inputEl() ){
21606 if(typeof w == 'number'){
21607 var aw = w - this.wrap.getFrameWidth('lr');
21608 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21611 if(typeof h == 'number'){
21612 var tbh = -11; // fixme it needs to tool bar size!
21613 for (var i =0; i < this.toolbars.length;i++) {
21614 // fixme - ask toolbars for heights?
21615 tbh += this.toolbars[i].el.getHeight();
21616 //if (this.toolbars[i].footer) {
21617 // tbh += this.toolbars[i].footer.el.getHeight();
21625 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21626 ah -= 5; // knock a few pixes off for look..
21627 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21631 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21632 this.editorcore.onResize(ew,eh);
21637 * Toggles the editor between standard and source edit mode.
21638 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21640 toggleSourceEdit : function(sourceEditMode)
21642 this.editorcore.toggleSourceEdit(sourceEditMode);
21644 if(this.editorcore.sourceEditMode){
21645 Roo.log('editor - showing textarea');
21648 // Roo.log(this.syncValue());
21650 this.inputEl().removeClass(['hide', 'x-hidden']);
21651 this.inputEl().dom.removeAttribute('tabIndex');
21652 this.inputEl().focus();
21654 Roo.log('editor - hiding textarea');
21656 // Roo.log(this.pushValue());
21659 this.inputEl().addClass(['hide', 'x-hidden']);
21660 this.inputEl().dom.setAttribute('tabIndex', -1);
21661 //this.deferFocus();
21664 if(this.resizable){
21665 this.setSize(this.wrap.getSize());
21668 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21671 // private (for BoxComponent)
21672 adjustSize : Roo.BoxComponent.prototype.adjustSize,
21674 // private (for BoxComponent)
21675 getResizeEl : function(){
21679 // private (for BoxComponent)
21680 getPositionEl : function(){
21685 initEvents : function(){
21686 this.originalValue = this.getValue();
21690 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21693 // markInvalid : Roo.emptyFn,
21695 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21698 // clearInvalid : Roo.emptyFn,
21700 setValue : function(v){
21701 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21702 this.editorcore.pushValue();
21707 deferFocus : function(){
21708 this.focus.defer(10, this);
21712 focus : function(){
21713 this.editorcore.focus();
21719 onDestroy : function(){
21725 for (var i =0; i < this.toolbars.length;i++) {
21726 // fixme - ask toolbars for heights?
21727 this.toolbars[i].onDestroy();
21730 this.wrap.dom.innerHTML = '';
21731 this.wrap.remove();
21736 onFirstFocus : function(){
21737 //Roo.log("onFirstFocus");
21738 this.editorcore.onFirstFocus();
21739 for (var i =0; i < this.toolbars.length;i++) {
21740 this.toolbars[i].onFirstFocus();
21746 syncValue : function()
21748 this.editorcore.syncValue();
21751 pushValue : function()
21753 this.editorcore.pushValue();
21757 // hide stuff that is not compatible
21771 * @event specialkey
21775 * @cfg {String} fieldClass @hide
21778 * @cfg {String} focusClass @hide
21781 * @cfg {String} autoCreate @hide
21784 * @cfg {String} inputType @hide
21787 * @cfg {String} invalidClass @hide
21790 * @cfg {String} invalidText @hide
21793 * @cfg {String} msgFx @hide
21796 * @cfg {String} validateOnBlur @hide
21805 Roo.namespace('Roo.bootstrap.htmleditor');
21807 * @class Roo.bootstrap.HtmlEditorToolbar1
21812 new Roo.bootstrap.HtmlEditor({
21815 new Roo.bootstrap.HtmlEditorToolbar1({
21816 disable : { fonts: 1 , format: 1, ..., ... , ...],
21822 * @cfg {Object} disable List of elements to disable..
21823 * @cfg {Array} btns List of additional buttons.
21827 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21830 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21833 Roo.apply(this, config);
21835 // default disabled, based on 'good practice'..
21836 this.disable = this.disable || {};
21837 Roo.applyIf(this.disable, {
21840 specialElements : true
21842 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21844 this.editor = config.editor;
21845 this.editorcore = config.editor.editorcore;
21847 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21849 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21850 // dont call parent... till later.
21852 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
21857 editorcore : false,
21862 "h1","h2","h3","h4","h5","h6",
21864 "abbr", "acronym", "address", "cite", "samp", "var",
21868 onRender : function(ct, position)
21870 // Roo.log("Call onRender: " + this.xtype);
21872 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21874 this.el.dom.style.marginBottom = '0';
21876 var editorcore = this.editorcore;
21877 var editor= this.editor;
21880 var btn = function(id,cmd , toggle, handler){
21882 var event = toggle ? 'toggle' : 'click';
21887 xns: Roo.bootstrap,
21890 enableToggle:toggle !== false,
21892 pressed : toggle ? false : null,
21895 a.listeners[toggle ? 'toggle' : 'click'] = function() {
21896 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
21905 xns: Roo.bootstrap,
21906 glyphicon : 'font',
21910 xns: Roo.bootstrap,
21914 Roo.each(this.formats, function(f) {
21915 style.menu.items.push({
21917 xns: Roo.bootstrap,
21918 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21923 editorcore.insertTag(this.tagname);
21930 children.push(style);
21933 btn('bold',false,true);
21934 btn('italic',false,true);
21935 btn('align-left', 'justifyleft',true);
21936 btn('align-center', 'justifycenter',true);
21937 btn('align-right' , 'justifyright',true);
21938 btn('link', false, false, function(btn) {
21939 //Roo.log("create link?");
21940 var url = prompt(this.createLinkText, this.defaultLinkValue);
21941 if(url && url != 'http:/'+'/'){
21942 this.editorcore.relayCmd('createlink', url);
21945 btn('list','insertunorderedlist',true);
21946 btn('pencil', false,true, function(btn){
21949 this.toggleSourceEdit(btn.pressed);
21955 xns: Roo.bootstrap,
21960 xns: Roo.bootstrap,
21965 cog.menu.items.push({
21967 xns: Roo.bootstrap,
21968 html : Clean styles,
21973 editorcore.insertTag(this.tagname);
21982 this.xtype = 'NavSimplebar';
21984 for(var i=0;i< children.length;i++) {
21986 this.buttons.add(this.addxtypeChild(children[i]));
21990 editor.on('editorevent', this.updateToolbar, this);
21992 onBtnClick : function(id)
21994 this.editorcore.relayCmd(id);
21995 this.editorcore.focus();
21999 * Protected method that will not generally be called directly. It triggers
22000 * a toolbar update by reading the markup state of the current selection in the editor.
22002 updateToolbar: function(){
22004 if(!this.editorcore.activated){
22005 this.editor.onFirstFocus(); // is this neeed?
22009 var btns = this.buttons;
22010 var doc = this.editorcore.doc;
22011 btns.get('bold').setActive(doc.queryCommandState('bold'));
22012 btns.get('italic').setActive(doc.queryCommandState('italic'));
22013 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22015 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22016 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22017 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22019 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22020 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22023 var ans = this.editorcore.getAllAncestors();
22024 if (this.formatCombo) {
22027 var store = this.formatCombo.store;
22028 this.formatCombo.setValue("");
22029 for (var i =0; i < ans.length;i++) {
22030 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22032 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22040 // hides menus... - so this cant be on a menu...
22041 Roo.bootstrap.MenuMgr.hideAll();
22043 Roo.bootstrap.MenuMgr.hideAll();
22044 //this.editorsyncValue();
22046 onFirstFocus: function() {
22047 this.buttons.each(function(item){
22051 toggleSourceEdit : function(sourceEditMode){
22054 if(sourceEditMode){
22055 Roo.log("disabling buttons");
22056 this.buttons.each( function(item){
22057 if(item.cmd != 'pencil'){
22063 Roo.log("enabling buttons");
22064 if(this.editorcore.initialized){
22065 this.buttons.each( function(item){
22071 Roo.log("calling toggole on editor");
22072 // tell the editor that it's been pressed..
22073 this.editor.toggleSourceEdit(sourceEditMode);
22083 * @class Roo.bootstrap.Table.AbstractSelectionModel
22084 * @extends Roo.util.Observable
22085 * Abstract base class for grid SelectionModels. It provides the interface that should be
22086 * implemented by descendant classes. This class should not be directly instantiated.
22089 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22090 this.locked = false;
22091 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22095 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22096 /** @ignore Called by the grid automatically. Do not call directly. */
22097 init : function(grid){
22103 * Locks the selections.
22106 this.locked = true;
22110 * Unlocks the selections.
22112 unlock : function(){
22113 this.locked = false;
22117 * Returns true if the selections are locked.
22118 * @return {Boolean}
22120 isLocked : function(){
22121 return this.locked;
22125 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22126 * @class Roo.bootstrap.Table.RowSelectionModel
22127 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22128 * It supports multiple selections and keyboard selection/navigation.
22130 * @param {Object} config
22133 Roo.bootstrap.Table.RowSelectionModel = function(config){
22134 Roo.apply(this, config);
22135 this.selections = new Roo.util.MixedCollection(false, function(o){
22140 this.lastActive = false;
22144 * @event selectionchange
22145 * Fires when the selection changes
22146 * @param {SelectionModel} this
22148 "selectionchange" : true,
22150 * @event afterselectionchange
22151 * Fires after the selection changes (eg. by key press or clicking)
22152 * @param {SelectionModel} this
22154 "afterselectionchange" : true,
22156 * @event beforerowselect
22157 * Fires when a row is selected being selected, return false to cancel.
22158 * @param {SelectionModel} this
22159 * @param {Number} rowIndex The selected index
22160 * @param {Boolean} keepExisting False if other selections will be cleared
22162 "beforerowselect" : true,
22165 * Fires when a row is selected.
22166 * @param {SelectionModel} this
22167 * @param {Number} rowIndex The selected index
22168 * @param {Roo.data.Record} r The record
22170 "rowselect" : true,
22172 * @event rowdeselect
22173 * Fires when a row is deselected.
22174 * @param {SelectionModel} this
22175 * @param {Number} rowIndex The selected index
22177 "rowdeselect" : true
22179 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22180 this.locked = false;
22183 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22185 * @cfg {Boolean} singleSelect
22186 * True to allow selection of only one row at a time (defaults to false)
22188 singleSelect : false,
22191 initEvents : function(){
22193 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22194 this.grid.on("mousedown", this.handleMouseDown, this);
22195 }else{ // allow click to work like normal
22196 this.grid.on("rowclick", this.handleDragableRowClick, this);
22199 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22200 "up" : function(e){
22202 this.selectPrevious(e.shiftKey);
22203 }else if(this.last !== false && this.lastActive !== false){
22204 var last = this.last;
22205 this.selectRange(this.last, this.lastActive-1);
22206 this.grid.getView().focusRow(this.lastActive);
22207 if(last !== false){
22211 this.selectFirstRow();
22213 this.fireEvent("afterselectionchange", this);
22215 "down" : function(e){
22217 this.selectNext(e.shiftKey);
22218 }else if(this.last !== false && this.lastActive !== false){
22219 var last = this.last;
22220 this.selectRange(this.last, this.lastActive+1);
22221 this.grid.getView().focusRow(this.lastActive);
22222 if(last !== false){
22226 this.selectFirstRow();
22228 this.fireEvent("afterselectionchange", this);
22233 var view = this.grid.view;
22234 view.on("refresh", this.onRefresh, this);
22235 view.on("rowupdated", this.onRowUpdated, this);
22236 view.on("rowremoved", this.onRemove, this);
22240 onRefresh : function(){
22241 var ds = this.grid.dataSource, i, v = this.grid.view;
22242 var s = this.selections;
22243 s.each(function(r){
22244 if((i = ds.indexOfId(r.id)) != -1){
22253 onRemove : function(v, index, r){
22254 this.selections.remove(r);
22258 onRowUpdated : function(v, index, r){
22259 if(this.isSelected(r)){
22260 v.onRowSelect(index);
22266 * @param {Array} records The records to select
22267 * @param {Boolean} keepExisting (optional) True to keep existing selections
22269 selectRecords : function(records, keepExisting){
22271 this.clearSelections();
22273 var ds = this.grid.dataSource;
22274 for(var i = 0, len = records.length; i < len; i++){
22275 this.selectRow(ds.indexOf(records[i]), true);
22280 * Gets the number of selected rows.
22283 getCount : function(){
22284 return this.selections.length;
22288 * Selects the first row in the grid.
22290 selectFirstRow : function(){
22295 * Select the last row.
22296 * @param {Boolean} keepExisting (optional) True to keep existing selections
22298 selectLastRow : function(keepExisting){
22299 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22303 * Selects the row immediately following the last selected row.
22304 * @param {Boolean} keepExisting (optional) True to keep existing selections
22306 selectNext : function(keepExisting){
22307 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22308 this.selectRow(this.last+1, keepExisting);
22309 this.grid.getView().focusRow(this.last);
22314 * Selects the row that precedes the last selected row.
22315 * @param {Boolean} keepExisting (optional) True to keep existing selections
22317 selectPrevious : function(keepExisting){
22319 this.selectRow(this.last-1, keepExisting);
22320 this.grid.getView().focusRow(this.last);
22325 * Returns the selected records
22326 * @return {Array} Array of selected records
22328 getSelections : function(){
22329 return [].concat(this.selections.items);
22333 * Returns the first selected record.
22336 getSelected : function(){
22337 return this.selections.itemAt(0);
22342 * Clears all selections.
22344 clearSelections : function(fast){
22349 var ds = this.grid.dataSource;
22350 var s = this.selections;
22351 s.each(function(r){
22352 this.deselectRow(ds.indexOfId(r.id));
22356 this.selections.clear();
22363 * Selects all rows.
22365 selectAll : function(){
22369 this.selections.clear();
22370 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22371 this.selectRow(i, true);
22376 * Returns True if there is a selection.
22377 * @return {Boolean}
22379 hasSelection : function(){
22380 return this.selections.length > 0;
22384 * Returns True if the specified row is selected.
22385 * @param {Number/Record} record The record or index of the record to check
22386 * @return {Boolean}
22388 isSelected : function(index){
22389 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22390 return (r && this.selections.key(r.id) ? true : false);
22394 * Returns True if the specified record id is selected.
22395 * @param {String} id The id of record to check
22396 * @return {Boolean}
22398 isIdSelected : function(id){
22399 return (this.selections.key(id) ? true : false);
22403 handleMouseDown : function(e, t){
22404 var view = this.grid.getView(), rowIndex;
22405 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22408 if(e.shiftKey && this.last !== false){
22409 var last = this.last;
22410 this.selectRange(last, rowIndex, e.ctrlKey);
22411 this.last = last; // reset the last
22412 view.focusRow(rowIndex);
22414 var isSelected = this.isSelected(rowIndex);
22415 if(e.button !== 0 && isSelected){
22416 view.focusRow(rowIndex);
22417 }else if(e.ctrlKey && isSelected){
22418 this.deselectRow(rowIndex);
22419 }else if(!isSelected){
22420 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22421 view.focusRow(rowIndex);
22424 this.fireEvent("afterselectionchange", this);
22427 handleDragableRowClick : function(grid, rowIndex, e)
22429 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22430 this.selectRow(rowIndex, false);
22431 grid.view.focusRow(rowIndex);
22432 this.fireEvent("afterselectionchange", this);
22437 * Selects multiple rows.
22438 * @param {Array} rows Array of the indexes of the row to select
22439 * @param {Boolean} keepExisting (optional) True to keep existing selections
22441 selectRows : function(rows, keepExisting){
22443 this.clearSelections();
22445 for(var i = 0, len = rows.length; i < len; i++){
22446 this.selectRow(rows[i], true);
22451 * Selects a range of rows. All rows in between startRow and endRow are also selected.
22452 * @param {Number} startRow The index of the first row in the range
22453 * @param {Number} endRow The index of the last row in the range
22454 * @param {Boolean} keepExisting (optional) True to retain existing selections
22456 selectRange : function(startRow, endRow, keepExisting){
22461 this.clearSelections();
22463 if(startRow <= endRow){
22464 for(var i = startRow; i <= endRow; i++){
22465 this.selectRow(i, true);
22468 for(var i = startRow; i >= endRow; i--){
22469 this.selectRow(i, true);
22475 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22476 * @param {Number} startRow The index of the first row in the range
22477 * @param {Number} endRow The index of the last row in the range
22479 deselectRange : function(startRow, endRow, preventViewNotify){
22483 for(var i = startRow; i <= endRow; i++){
22484 this.deselectRow(i, preventViewNotify);
22490 * @param {Number} row The index of the row to select
22491 * @param {Boolean} keepExisting (optional) True to keep existing selections
22493 selectRow : function(index, keepExisting, preventViewNotify){
22494 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22497 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22498 if(!keepExisting || this.singleSelect){
22499 this.clearSelections();
22501 var r = this.grid.dataSource.getAt(index);
22502 this.selections.add(r);
22503 this.last = this.lastActive = index;
22504 if(!preventViewNotify){
22505 this.grid.getView().onRowSelect(index);
22507 this.fireEvent("rowselect", this, index, r);
22508 this.fireEvent("selectionchange", this);
22514 * @param {Number} row The index of the row to deselect
22516 deselectRow : function(index, preventViewNotify){
22520 if(this.last == index){
22523 if(this.lastActive == index){
22524 this.lastActive = false;
22526 var r = this.grid.dataSource.getAt(index);
22527 this.selections.remove(r);
22528 if(!preventViewNotify){
22529 this.grid.getView().onRowDeselect(index);
22531 this.fireEvent("rowdeselect", this, index);
22532 this.fireEvent("selectionchange", this);
22536 restoreLast : function(){
22538 this.last = this._last;
22543 acceptsNav : function(row, col, cm){
22544 return !cm.isHidden(col) && cm.isCellEditable(col, row);
22548 onEditorKey : function(field, e){
22549 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22554 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22556 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22558 }else if(k == e.ENTER && !e.ctrlKey){
22562 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22564 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22566 }else if(k == e.ESC){
22570 g.startEditing(newCell[0], newCell[1]);
22575 * Ext JS Library 1.1.1
22576 * Copyright(c) 2006-2007, Ext JS, LLC.
22578 * Originally Released Under LGPL - original licence link has changed is not relivant.
22581 * <script type="text/javascript">
22585 * @class Roo.bootstrap.PagingToolbar
22586 * @extends Roo.bootstrap.NavSimplebar
22587 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22589 * Create a new PagingToolbar
22590 * @param {Object} config The config object
22591 * @param {Roo.data.Store} store
22593 Roo.bootstrap.PagingToolbar = function(config)
22595 // old args format still supported... - xtype is prefered..
22596 // created from xtype...
22598 this.ds = config.dataSource;
22600 if (config.store && !this.ds) {
22601 this.store= Roo.factory(config.store, Roo.data);
22602 this.ds = this.store;
22603 this.ds.xmodule = this.xmodule || false;
22606 this.toolbarItems = [];
22607 if (config.items) {
22608 this.toolbarItems = config.items;
22611 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22616 this.bind(this.ds);
22619 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22623 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22625 * @cfg {Roo.data.Store} dataSource
22626 * The underlying data store providing the paged data
22629 * @cfg {String/HTMLElement/Element} container
22630 * container The id or element that will contain the toolbar
22633 * @cfg {Boolean} displayInfo
22634 * True to display the displayMsg (defaults to false)
22637 * @cfg {Number} pageSize
22638 * The number of records to display per page (defaults to 20)
22642 * @cfg {String} displayMsg
22643 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22645 displayMsg : 'Displaying {0} - {1} of {2}',
22647 * @cfg {String} emptyMsg
22648 * The message to display when no records are found (defaults to "No data to display")
22650 emptyMsg : 'No data to display',
22652 * Customizable piece of the default paging text (defaults to "Page")
22655 beforePageText : "Page",
22657 * Customizable piece of the default paging text (defaults to "of %0")
22660 afterPageText : "of {0}",
22662 * Customizable piece of the default paging text (defaults to "First Page")
22665 firstText : "First Page",
22667 * Customizable piece of the default paging text (defaults to "Previous Page")
22670 prevText : "Previous Page",
22672 * Customizable piece of the default paging text (defaults to "Next Page")
22675 nextText : "Next Page",
22677 * Customizable piece of the default paging text (defaults to "Last Page")
22680 lastText : "Last Page",
22682 * Customizable piece of the default paging text (defaults to "Refresh")
22685 refreshText : "Refresh",
22689 onRender : function(ct, position)
22691 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22692 this.navgroup.parentId = this.id;
22693 this.navgroup.onRender(this.el, null);
22694 // add the buttons to the navgroup
22696 if(this.displayInfo){
22697 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22698 this.displayEl = this.el.select('.x-paging-info', true).first();
22699 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22700 // this.displayEl = navel.el.select('span',true).first();
22706 Roo.each(_this.buttons, function(e){ // this might need to use render????
22707 Roo.factory(e).onRender(_this.el, null);
22711 Roo.each(_this.toolbarItems, function(e) {
22712 _this.navgroup.addItem(e);
22716 this.first = this.navgroup.addItem({
22717 tooltip: this.firstText,
22719 icon : 'fa fa-backward',
22721 preventDefault: true,
22722 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22725 this.prev = this.navgroup.addItem({
22726 tooltip: this.prevText,
22728 icon : 'fa fa-step-backward',
22730 preventDefault: true,
22731 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
22733 //this.addSeparator();
22736 var field = this.navgroup.addItem( {
22738 cls : 'x-paging-position',
22740 html : this.beforePageText +
22741 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22742 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
22745 this.field = field.el.select('input', true).first();
22746 this.field.on("keydown", this.onPagingKeydown, this);
22747 this.field.on("focus", function(){this.dom.select();});
22750 this.afterTextEl = field.el.select('.x-paging-after',true).first();
22751 //this.field.setHeight(18);
22752 //this.addSeparator();
22753 this.next = this.navgroup.addItem({
22754 tooltip: this.nextText,
22756 html : ' <i class="fa fa-step-forward">',
22758 preventDefault: true,
22759 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
22761 this.last = this.navgroup.addItem({
22762 tooltip: this.lastText,
22763 icon : 'fa fa-forward',
22766 preventDefault: true,
22767 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
22769 //this.addSeparator();
22770 this.loading = this.navgroup.addItem({
22771 tooltip: this.refreshText,
22772 icon: 'fa fa-refresh',
22773 preventDefault: true,
22774 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22780 updateInfo : function(){
22781 if(this.displayEl){
22782 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22783 var msg = count == 0 ?
22787 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
22789 this.displayEl.update(msg);
22794 onLoad : function(ds, r, o){
22795 this.cursor = o.params ? o.params.start : 0;
22796 var d = this.getPageData(),
22800 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22801 this.field.dom.value = ap;
22802 this.first.setDisabled(ap == 1);
22803 this.prev.setDisabled(ap == 1);
22804 this.next.setDisabled(ap == ps);
22805 this.last.setDisabled(ap == ps);
22806 this.loading.enable();
22811 getPageData : function(){
22812 var total = this.ds.getTotalCount();
22815 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22816 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22821 onLoadError : function(){
22822 this.loading.enable();
22826 onPagingKeydown : function(e){
22827 var k = e.getKey();
22828 var d = this.getPageData();
22830 var v = this.field.dom.value, pageNum;
22831 if(!v || isNaN(pageNum = parseInt(v, 10))){
22832 this.field.dom.value = d.activePage;
22835 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22836 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22839 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))
22841 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22842 this.field.dom.value = pageNum;
22843 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22846 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22848 var v = this.field.dom.value, pageNum;
22849 var increment = (e.shiftKey) ? 10 : 1;
22850 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22853 if(!v || isNaN(pageNum = parseInt(v, 10))) {
22854 this.field.dom.value = d.activePage;
22857 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22859 this.field.dom.value = parseInt(v, 10) + increment;
22860 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22861 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22868 beforeLoad : function(){
22870 this.loading.disable();
22875 onClick : function(which){
22884 ds.load({params:{start: 0, limit: this.pageSize}});
22887 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22890 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22893 var total = ds.getTotalCount();
22894 var extra = total % this.pageSize;
22895 var lastStart = extra ? (total - extra) : total-this.pageSize;
22896 ds.load({params:{start: lastStart, limit: this.pageSize}});
22899 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22905 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22906 * @param {Roo.data.Store} store The data store to unbind
22908 unbind : function(ds){
22909 ds.un("beforeload", this.beforeLoad, this);
22910 ds.un("load", this.onLoad, this);
22911 ds.un("loadexception", this.onLoadError, this);
22912 ds.un("remove", this.updateInfo, this);
22913 ds.un("add", this.updateInfo, this);
22914 this.ds = undefined;
22918 * Binds the paging toolbar to the specified {@link Roo.data.Store}
22919 * @param {Roo.data.Store} store The data store to bind
22921 bind : function(ds){
22922 ds.on("beforeload", this.beforeLoad, this);
22923 ds.on("load", this.onLoad, this);
22924 ds.on("loadexception", this.onLoadError, this);
22925 ds.on("remove", this.updateInfo, this);
22926 ds.on("add", this.updateInfo, this);
22937 * @class Roo.bootstrap.MessageBar
22938 * @extends Roo.bootstrap.Component
22939 * Bootstrap MessageBar class
22940 * @cfg {String} html contents of the MessageBar
22941 * @cfg {String} weight (info | success | warning | danger) default info
22942 * @cfg {String} beforeClass insert the bar before the given class
22943 * @cfg {Boolean} closable (true | false) default false
22944 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22947 * Create a new Element
22948 * @param {Object} config The config object
22951 Roo.bootstrap.MessageBar = function(config){
22952 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
22961 beforeClass: 'bootstrap-sticky-wrap',
22963 getAutoCreate : function(){
22967 cls: 'alert alert-dismissable alert-' + this.weight,
22972 html: this.html || ''
22978 cfg.cls += ' alert-messages-fixed';
22992 onRender : function(ct, position)
22994 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22997 var cfg = Roo.apply({}, this.getAutoCreate());
23001 cfg.cls += ' ' + this.cls;
23004 cfg.style = this.style;
23006 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23008 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23011 this.el.select('>button.close').on('click', this.hide, this);
23017 if (!this.rendered) {
23023 this.fireEvent('show', this);
23029 if (!this.rendered) {
23035 this.fireEvent('hide', this);
23038 update : function()
23040 // var e = this.el.dom.firstChild;
23042 // if(this.closable){
23043 // e = e.nextSibling;
23046 // e.data = this.html || '';
23048 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23064 * @class Roo.bootstrap.Graph
23065 * @extends Roo.bootstrap.Component
23066 * Bootstrap Graph class
23070 @cfg {String} graphtype bar | vbar | pie
23071 @cfg {number} g_x coodinator | centre x (pie)
23072 @cfg {number} g_y coodinator | centre y (pie)
23073 @cfg {number} g_r radius (pie)
23074 @cfg {number} g_height height of the chart (respected by all elements in the set)
23075 @cfg {number} g_width width of the chart (respected by all elements in the set)
23076 @cfg {Object} title The title of the chart
23079 -opts (object) options for the chart
23081 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23082 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23084 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.
23085 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23087 o stretch (boolean)
23089 -opts (object) options for the pie
23092 o startAngle (number)
23093 o endAngle (number)
23097 * Create a new Input
23098 * @param {Object} config The config object
23101 Roo.bootstrap.Graph = function(config){
23102 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23108 * The img click event for the img.
23109 * @param {Roo.EventObject} e
23115 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23126 //g_colors: this.colors,
23133 getAutoCreate : function(){
23144 onRender : function(ct,position){
23147 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23149 if (typeof(Raphael) == 'undefined') {
23150 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23154 this.raphael = Raphael(this.el.dom);
23156 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23157 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23158 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23159 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23161 r.text(160, 10, "Single Series Chart").attr(txtattr);
23162 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23163 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23164 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23166 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23167 r.barchart(330, 10, 300, 220, data1);
23168 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23169 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23172 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23173 // r.barchart(30, 30, 560, 250, xdata, {
23174 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23175 // axis : "0 0 1 1",
23176 // axisxlabels : xdata
23177 // //yvalues : cols,
23180 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23182 // this.load(null,xdata,{
23183 // axis : "0 0 1 1",
23184 // axisxlabels : xdata
23189 load : function(graphtype,xdata,opts)
23191 this.raphael.clear();
23193 graphtype = this.graphtype;
23198 var r = this.raphael,
23199 fin = function () {
23200 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23202 fout = function () {
23203 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23205 pfin = function() {
23206 this.sector.stop();
23207 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23210 this.label[0].stop();
23211 this.label[0].attr({ r: 7.5 });
23212 this.label[1].attr({ "font-weight": 800 });
23215 pfout = function() {
23216 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23219 this.label[0].animate({ r: 5 }, 500, "bounce");
23220 this.label[1].attr({ "font-weight": 400 });
23226 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23229 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23232 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23233 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23235 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23242 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23247 setTitle: function(o)
23252 initEvents: function() {
23255 this.el.on('click', this.onClick, this);
23259 onClick : function(e)
23261 Roo.log('img onclick');
23262 this.fireEvent('click', this, e);
23274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23277 * @class Roo.bootstrap.dash.NumberBox
23278 * @extends Roo.bootstrap.Component
23279 * Bootstrap NumberBox class
23280 * @cfg {String} headline Box headline
23281 * @cfg {String} content Box content
23282 * @cfg {String} icon Box icon
23283 * @cfg {String} footer Footer text
23284 * @cfg {String} fhref Footer href
23287 * Create a new NumberBox
23288 * @param {Object} config The config object
23292 Roo.bootstrap.dash.NumberBox = function(config){
23293 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23297 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
23306 getAutoCreate : function(){
23310 cls : 'small-box ',
23318 cls : 'roo-headline',
23319 html : this.headline
23323 cls : 'roo-content',
23324 html : this.content
23338 cls : 'ion ' + this.icon
23347 cls : 'small-box-footer',
23348 href : this.fhref || '#',
23352 cfg.cn.push(footer);
23359 onRender : function(ct,position){
23360 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23367 setHeadline: function (value)
23369 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23372 setFooter: function (value, href)
23374 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23377 this.el.select('a.small-box-footer',true).first().attr('href', href);
23382 setContent: function (value)
23384 this.el.select('.roo-content',true).first().dom.innerHTML = value;
23387 initEvents: function()
23401 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23404 * @class Roo.bootstrap.dash.TabBox
23405 * @extends Roo.bootstrap.Component
23406 * Bootstrap TabBox class
23407 * @cfg {String} title Title of the TabBox
23408 * @cfg {String} icon Icon of the TabBox
23409 * @cfg {Boolean} showtabs (true|false) show the tabs default true
23410 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23413 * Create a new TabBox
23414 * @param {Object} config The config object
23418 Roo.bootstrap.dash.TabBox = function(config){
23419 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23424 * When a pane is added
23425 * @param {Roo.bootstrap.dash.TabPane} pane
23429 * @event activatepane
23430 * When a pane is activated
23431 * @param {Roo.bootstrap.dash.TabPane} pane
23433 "activatepane" : true
23441 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
23446 tabScrollable : false,
23448 getChildContainer : function()
23450 return this.el.select('.tab-content', true).first();
23453 getAutoCreate : function(){
23457 cls: 'pull-left header',
23465 cls: 'fa ' + this.icon
23471 cls: 'nav nav-tabs pull-right',
23477 if(this.tabScrollable){
23484 cls: 'nav nav-tabs pull-right',
23495 cls: 'nav-tabs-custom',
23500 cls: 'tab-content no-padding',
23508 initEvents : function()
23510 //Roo.log('add add pane handler');
23511 this.on('addpane', this.onAddPane, this);
23514 * Updates the box title
23515 * @param {String} html to set the title to.
23517 setTitle : function(value)
23519 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23521 onAddPane : function(pane)
23523 this.panes.push(pane);
23524 //Roo.log('addpane');
23526 // tabs are rendere left to right..
23527 if(!this.showtabs){
23531 var ctr = this.el.select('.nav-tabs', true).first();
23534 var existing = ctr.select('.nav-tab',true);
23535 var qty = existing.getCount();;
23538 var tab = ctr.createChild({
23540 cls : 'nav-tab' + (qty ? '' : ' active'),
23548 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23551 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23553 pane.el.addClass('active');
23558 onTabClick : function(ev,un,ob,pane)
23560 //Roo.log('tab - prev default');
23561 ev.preventDefault();
23564 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23565 pane.tab.addClass('active');
23566 //Roo.log(pane.title);
23567 this.getChildContainer().select('.tab-pane',true).removeClass('active');
23568 // technically we should have a deactivate event.. but maybe add later.
23569 // and it should not de-activate the selected tab...
23570 this.fireEvent('activatepane', pane);
23571 pane.el.addClass('active');
23572 pane.fireEvent('activate');
23577 getActivePane : function()
23580 Roo.each(this.panes, function(p) {
23581 if(p.el.hasClass('active')){
23602 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23604 * @class Roo.bootstrap.TabPane
23605 * @extends Roo.bootstrap.Component
23606 * Bootstrap TabPane class
23607 * @cfg {Boolean} active (false | true) Default false
23608 * @cfg {String} title title of panel
23612 * Create a new TabPane
23613 * @param {Object} config The config object
23616 Roo.bootstrap.dash.TabPane = function(config){
23617 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23623 * When a pane is activated
23624 * @param {Roo.bootstrap.dash.TabPane} pane
23631 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
23636 // the tabBox that this is attached to.
23639 getAutoCreate : function()
23647 cfg.cls += ' active';
23652 initEvents : function()
23654 //Roo.log('trigger add pane handler');
23655 this.parent().fireEvent('addpane', this)
23659 * Updates the tab title
23660 * @param {String} html to set the title to.
23662 setTitle: function(str)
23668 this.tab.select('a', true).first().dom.innerHTML = str;
23685 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23688 * @class Roo.bootstrap.menu.Menu
23689 * @extends Roo.bootstrap.Component
23690 * Bootstrap Menu class - container for Menu
23691 * @cfg {String} html Text of the menu
23692 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23693 * @cfg {String} icon Font awesome icon
23694 * @cfg {String} pos Menu align to (top | bottom) default bottom
23698 * Create a new Menu
23699 * @param {Object} config The config object
23703 Roo.bootstrap.menu.Menu = function(config){
23704 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23708 * @event beforeshow
23709 * Fires before this menu is displayed
23710 * @param {Roo.bootstrap.menu.Menu} this
23714 * @event beforehide
23715 * Fires before this menu is hidden
23716 * @param {Roo.bootstrap.menu.Menu} this
23721 * Fires after this menu is displayed
23722 * @param {Roo.bootstrap.menu.Menu} this
23727 * Fires after this menu is hidden
23728 * @param {Roo.bootstrap.menu.Menu} this
23733 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23734 * @param {Roo.bootstrap.menu.Menu} this
23735 * @param {Roo.EventObject} e
23742 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
23746 weight : 'default',
23751 getChildContainer : function() {
23752 if(this.isSubMenu){
23756 return this.el.select('ul.dropdown-menu', true).first();
23759 getAutoCreate : function()
23764 cls : 'roo-menu-text',
23772 cls : 'fa ' + this.icon
23783 cls : 'dropdown-button btn btn-' + this.weight,
23788 cls : 'dropdown-toggle btn btn-' + this.weight,
23798 cls : 'dropdown-menu'
23804 if(this.pos == 'top'){
23805 cfg.cls += ' dropup';
23808 if(this.isSubMenu){
23811 cls : 'dropdown-menu'
23818 onRender : function(ct, position)
23820 this.isSubMenu = ct.hasClass('dropdown-submenu');
23822 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23825 initEvents : function()
23827 if(this.isSubMenu){
23831 this.hidden = true;
23833 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23834 this.triggerEl.on('click', this.onTriggerPress, this);
23836 this.buttonEl = this.el.select('button.dropdown-button', true).first();
23837 this.buttonEl.on('click', this.onClick, this);
23843 if(this.isSubMenu){
23847 return this.el.select('ul.dropdown-menu', true).first();
23850 onClick : function(e)
23852 this.fireEvent("click", this, e);
23855 onTriggerPress : function(e)
23857 if (this.isVisible()) {
23864 isVisible : function(){
23865 return !this.hidden;
23870 this.fireEvent("beforeshow", this);
23872 this.hidden = false;
23873 this.el.addClass('open');
23875 Roo.get(document).on("mouseup", this.onMouseUp, this);
23877 this.fireEvent("show", this);
23884 this.fireEvent("beforehide", this);
23886 this.hidden = true;
23887 this.el.removeClass('open');
23889 Roo.get(document).un("mouseup", this.onMouseUp);
23891 this.fireEvent("hide", this);
23894 onMouseUp : function()
23908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23911 * @class Roo.bootstrap.menu.Item
23912 * @extends Roo.bootstrap.Component
23913 * Bootstrap MenuItem class
23914 * @cfg {Boolean} submenu (true | false) default false
23915 * @cfg {String} html text of the item
23916 * @cfg {String} href the link
23917 * @cfg {Boolean} disable (true | false) default false
23918 * @cfg {Boolean} preventDefault (true | false) default true
23919 * @cfg {String} icon Font awesome icon
23920 * @cfg {String} pos Submenu align to (left | right) default right
23924 * Create a new Item
23925 * @param {Object} config The config object
23929 Roo.bootstrap.menu.Item = function(config){
23930 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23934 * Fires when the mouse is hovering over this menu
23935 * @param {Roo.bootstrap.menu.Item} this
23936 * @param {Roo.EventObject} e
23941 * Fires when the mouse exits this menu
23942 * @param {Roo.bootstrap.menu.Item} this
23943 * @param {Roo.EventObject} e
23949 * The raw click event for the entire grid.
23950 * @param {Roo.EventObject} e
23956 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
23961 preventDefault: true,
23966 getAutoCreate : function()
23971 cls : 'roo-menu-item-text',
23979 cls : 'fa ' + this.icon
23988 href : this.href || '#',
23995 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23999 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24001 if(this.pos == 'left'){
24002 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24009 initEvents : function()
24011 this.el.on('mouseover', this.onMouseOver, this);
24012 this.el.on('mouseout', this.onMouseOut, this);
24014 this.el.select('a', true).first().on('click', this.onClick, this);
24018 onClick : function(e)
24020 if(this.preventDefault){
24021 e.preventDefault();
24024 this.fireEvent("click", this, e);
24027 onMouseOver : function(e)
24029 if(this.submenu && this.pos == 'left'){
24030 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24033 this.fireEvent("mouseover", this, e);
24036 onMouseOut : function(e)
24038 this.fireEvent("mouseout", this, e);
24050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24053 * @class Roo.bootstrap.menu.Separator
24054 * @extends Roo.bootstrap.Component
24055 * Bootstrap Separator class
24058 * Create a new Separator
24059 * @param {Object} config The config object
24063 Roo.bootstrap.menu.Separator = function(config){
24064 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24067 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24069 getAutoCreate : function(){
24090 * @class Roo.bootstrap.Tooltip
24091 * Bootstrap Tooltip class
24092 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24093 * to determine which dom element triggers the tooltip.
24095 * It needs to add support for additional attributes like tooltip-position
24098 * Create a new Toolti
24099 * @param {Object} config The config object
24102 Roo.bootstrap.Tooltip = function(config){
24103 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24106 Roo.apply(Roo.bootstrap.Tooltip, {
24108 * @function init initialize tooltip monitoring.
24112 currentTip : false,
24113 currentRegion : false,
24119 Roo.get(document).on('mouseover', this.enter ,this);
24120 Roo.get(document).on('mouseout', this.leave, this);
24123 this.currentTip = new Roo.bootstrap.Tooltip();
24126 enter : function(ev)
24128 var dom = ev.getTarget();
24130 //Roo.log(['enter',dom]);
24131 var el = Roo.fly(dom);
24132 if (this.currentEl) {
24134 //Roo.log(this.currentEl);
24135 //Roo.log(this.currentEl.contains(dom));
24136 if (this.currentEl == el) {
24139 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24145 if (this.currentTip.el) {
24146 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24151 // you can not look for children, as if el is the body.. then everythign is the child..
24152 if (!el.attr('tooltip')) { //
24153 if (!el.select("[tooltip]").elements.length) {
24156 // is the mouse over this child...?
24157 bindEl = el.select("[tooltip]").first();
24158 var xy = ev.getXY();
24159 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24160 //Roo.log("not in region.");
24163 //Roo.log("child element over..");
24166 this.currentEl = bindEl;
24167 this.currentTip.bind(bindEl);
24168 this.currentRegion = Roo.lib.Region.getRegion(dom);
24169 this.currentTip.enter();
24172 leave : function(ev)
24174 var dom = ev.getTarget();
24175 //Roo.log(['leave',dom]);
24176 if (!this.currentEl) {
24181 if (dom != this.currentEl.dom) {
24184 var xy = ev.getXY();
24185 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24188 // only activate leave if mouse cursor is outside... bounding box..
24193 if (this.currentTip) {
24194 this.currentTip.leave();
24196 //Roo.log('clear currentEl');
24197 this.currentEl = false;
24202 'left' : ['r-l', [-2,0], 'right'],
24203 'right' : ['l-r', [2,0], 'left'],
24204 'bottom' : ['t-b', [0,2], 'top'],
24205 'top' : [ 'b-t', [0,-2], 'bottom']
24211 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24216 delay : null, // can be { show : 300 , hide: 500}
24220 hoverState : null, //???
24222 placement : 'bottom',
24224 getAutoCreate : function(){
24231 cls : 'tooltip-arrow'
24234 cls : 'tooltip-inner'
24241 bind : function(el)
24247 enter : function () {
24249 if (this.timeout != null) {
24250 clearTimeout(this.timeout);
24253 this.hoverState = 'in';
24254 //Roo.log("enter - show");
24255 if (!this.delay || !this.delay.show) {
24260 this.timeout = setTimeout(function () {
24261 if (_t.hoverState == 'in') {
24264 }, this.delay.show);
24268 clearTimeout(this.timeout);
24270 this.hoverState = 'out';
24271 if (!this.delay || !this.delay.hide) {
24277 this.timeout = setTimeout(function () {
24278 //Roo.log("leave - timeout");
24280 if (_t.hoverState == 'out') {
24282 Roo.bootstrap.Tooltip.currentEl = false;
24290 this.render(document.body);
24293 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24295 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24297 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24299 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24301 var placement = typeof this.placement == 'function' ?
24302 this.placement.call(this, this.el, on_el) :
24305 var autoToken = /\s?auto?\s?/i;
24306 var autoPlace = autoToken.test(placement);
24308 placement = placement.replace(autoToken, '') || 'top';
24312 //this.el.setXY([0,0]);
24314 //this.el.dom.style.display='block';
24316 //this.el.appendTo(on_el);
24318 var p = this.getPosition();
24319 var box = this.el.getBox();
24325 var align = Roo.bootstrap.Tooltip.alignment[placement];
24327 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24329 if(placement == 'top' || placement == 'bottom'){
24331 placement = 'right';
24334 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24335 placement = 'left';
24339 align = Roo.bootstrap.Tooltip.alignment[placement];
24341 this.el.alignTo(this.bindEl, align[0],align[1]);
24342 //var arrow = this.el.select('.arrow',true).first();
24343 //arrow.set(align[2],
24345 this.el.addClass(placement);
24347 this.el.addClass('in fade');
24349 this.hoverState = null;
24351 if (this.el.hasClass('fade')) {
24362 //this.el.setXY([0,0]);
24363 this.el.removeClass('in');
24379 * @class Roo.bootstrap.LocationPicker
24380 * @extends Roo.bootstrap.Component
24381 * Bootstrap LocationPicker class
24382 * @cfg {Number} latitude Position when init default 0
24383 * @cfg {Number} longitude Position when init default 0
24384 * @cfg {Number} zoom default 15
24385 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24386 * @cfg {Boolean} mapTypeControl default false
24387 * @cfg {Boolean} disableDoubleClickZoom default false
24388 * @cfg {Boolean} scrollwheel default true
24389 * @cfg {Boolean} streetViewControl default false
24390 * @cfg {Number} radius default 0
24391 * @cfg {String} locationName
24392 * @cfg {Boolean} draggable default true
24393 * @cfg {Boolean} enableAutocomplete default false
24394 * @cfg {Boolean} enableReverseGeocode default true
24395 * @cfg {String} markerTitle
24398 * Create a new LocationPicker
24399 * @param {Object} config The config object
24403 Roo.bootstrap.LocationPicker = function(config){
24405 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24410 * Fires when the picker initialized.
24411 * @param {Roo.bootstrap.LocationPicker} this
24412 * @param {Google Location} location
24416 * @event positionchanged
24417 * Fires when the picker position changed.
24418 * @param {Roo.bootstrap.LocationPicker} this
24419 * @param {Google Location} location
24421 positionchanged : true,
24424 * Fires when the map resize.
24425 * @param {Roo.bootstrap.LocationPicker} this
24430 * Fires when the map show.
24431 * @param {Roo.bootstrap.LocationPicker} this
24436 * Fires when the map hide.
24437 * @param {Roo.bootstrap.LocationPicker} this
24442 * Fires when click the map.
24443 * @param {Roo.bootstrap.LocationPicker} this
24444 * @param {Map event} e
24448 * @event mapRightClick
24449 * Fires when right click the map.
24450 * @param {Roo.bootstrap.LocationPicker} this
24451 * @param {Map event} e
24453 mapRightClick : true,
24455 * @event markerClick
24456 * Fires when click the marker.
24457 * @param {Roo.bootstrap.LocationPicker} this
24458 * @param {Map event} e
24460 markerClick : true,
24462 * @event markerRightClick
24463 * Fires when right click the marker.
24464 * @param {Roo.bootstrap.LocationPicker} this
24465 * @param {Map event} e
24467 markerRightClick : true,
24469 * @event OverlayViewDraw
24470 * Fires when OverlayView Draw
24471 * @param {Roo.bootstrap.LocationPicker} this
24473 OverlayViewDraw : true,
24475 * @event OverlayViewOnAdd
24476 * Fires when OverlayView Draw
24477 * @param {Roo.bootstrap.LocationPicker} this
24479 OverlayViewOnAdd : true,
24481 * @event OverlayViewOnRemove
24482 * Fires when OverlayView Draw
24483 * @param {Roo.bootstrap.LocationPicker} this
24485 OverlayViewOnRemove : true,
24487 * @event OverlayViewShow
24488 * Fires when OverlayView Draw
24489 * @param {Roo.bootstrap.LocationPicker} this
24490 * @param {Pixel} cpx
24492 OverlayViewShow : true,
24494 * @event OverlayViewHide
24495 * Fires when OverlayView Draw
24496 * @param {Roo.bootstrap.LocationPicker} this
24498 OverlayViewHide : true,
24500 * @event loadexception
24501 * Fires when load google lib failed.
24502 * @param {Roo.bootstrap.LocationPicker} this
24504 loadexception : true
24509 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
24511 gMapContext: false,
24517 mapTypeControl: false,
24518 disableDoubleClickZoom: false,
24520 streetViewControl: false,
24524 enableAutocomplete: false,
24525 enableReverseGeocode: true,
24528 getAutoCreate: function()
24533 cls: 'roo-location-picker'
24539 initEvents: function(ct, position)
24541 if(!this.el.getWidth() || this.isApplied()){
24545 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24550 initial: function()
24552 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24553 this.fireEvent('loadexception', this);
24557 if(!this.mapTypeId){
24558 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24561 this.gMapContext = this.GMapContext();
24563 this.initOverlayView();
24565 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24569 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24570 _this.setPosition(_this.gMapContext.marker.position);
24573 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24574 _this.fireEvent('mapClick', this, event);
24578 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24579 _this.fireEvent('mapRightClick', this, event);
24583 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24584 _this.fireEvent('markerClick', this, event);
24588 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24589 _this.fireEvent('markerRightClick', this, event);
24593 this.setPosition(this.gMapContext.location);
24595 this.fireEvent('initial', this, this.gMapContext.location);
24598 initOverlayView: function()
24602 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24606 _this.fireEvent('OverlayViewDraw', _this);
24611 _this.fireEvent('OverlayViewOnAdd', _this);
24614 onRemove: function()
24616 _this.fireEvent('OverlayViewOnRemove', _this);
24619 show: function(cpx)
24621 _this.fireEvent('OverlayViewShow', _this, cpx);
24626 _this.fireEvent('OverlayViewHide', _this);
24632 fromLatLngToContainerPixel: function(event)
24634 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24637 isApplied: function()
24639 return this.getGmapContext() == false ? false : true;
24642 getGmapContext: function()
24644 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24647 GMapContext: function()
24649 var position = new google.maps.LatLng(this.latitude, this.longitude);
24651 var _map = new google.maps.Map(this.el.dom, {
24654 mapTypeId: this.mapTypeId,
24655 mapTypeControl: this.mapTypeControl,
24656 disableDoubleClickZoom: this.disableDoubleClickZoom,
24657 scrollwheel: this.scrollwheel,
24658 streetViewControl: this.streetViewControl,
24659 locationName: this.locationName,
24660 draggable: this.draggable,
24661 enableAutocomplete: this.enableAutocomplete,
24662 enableReverseGeocode: this.enableReverseGeocode
24665 var _marker = new google.maps.Marker({
24666 position: position,
24668 title: this.markerTitle,
24669 draggable: this.draggable
24676 location: position,
24677 radius: this.radius,
24678 locationName: this.locationName,
24679 addressComponents: {
24680 formatted_address: null,
24681 addressLine1: null,
24682 addressLine2: null,
24684 streetNumber: null,
24688 stateOrProvince: null
24691 domContainer: this.el.dom,
24692 geodecoder: new google.maps.Geocoder()
24696 drawCircle: function(center, radius, options)
24698 if (this.gMapContext.circle != null) {
24699 this.gMapContext.circle.setMap(null);
24703 options = Roo.apply({}, options, {
24704 strokeColor: "#0000FF",
24705 strokeOpacity: .35,
24707 fillColor: "#0000FF",
24711 options.map = this.gMapContext.map;
24712 options.radius = radius;
24713 options.center = center;
24714 this.gMapContext.circle = new google.maps.Circle(options);
24715 return this.gMapContext.circle;
24721 setPosition: function(location)
24723 this.gMapContext.location = location;
24724 this.gMapContext.marker.setPosition(location);
24725 this.gMapContext.map.panTo(location);
24726 this.drawCircle(location, this.gMapContext.radius, {});
24730 if (this.gMapContext.settings.enableReverseGeocode) {
24731 this.gMapContext.geodecoder.geocode({
24732 latLng: this.gMapContext.location
24733 }, function(results, status) {
24735 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24736 _this.gMapContext.locationName = results[0].formatted_address;
24737 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24739 _this.fireEvent('positionchanged', this, location);
24746 this.fireEvent('positionchanged', this, location);
24751 google.maps.event.trigger(this.gMapContext.map, "resize");
24753 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24755 this.fireEvent('resize', this);
24758 setPositionByLatLng: function(latitude, longitude)
24760 this.setPosition(new google.maps.LatLng(latitude, longitude));
24763 getCurrentPosition: function()
24766 latitude: this.gMapContext.location.lat(),
24767 longitude: this.gMapContext.location.lng()
24771 getAddressName: function()
24773 return this.gMapContext.locationName;
24776 getAddressComponents: function()
24778 return this.gMapContext.addressComponents;
24781 address_component_from_google_geocode: function(address_components)
24785 for (var i = 0; i < address_components.length; i++) {
24786 var component = address_components[i];
24787 if (component.types.indexOf("postal_code") >= 0) {
24788 result.postalCode = component.short_name;
24789 } else if (component.types.indexOf("street_number") >= 0) {
24790 result.streetNumber = component.short_name;
24791 } else if (component.types.indexOf("route") >= 0) {
24792 result.streetName = component.short_name;
24793 } else if (component.types.indexOf("neighborhood") >= 0) {
24794 result.city = component.short_name;
24795 } else if (component.types.indexOf("locality") >= 0) {
24796 result.city = component.short_name;
24797 } else if (component.types.indexOf("sublocality") >= 0) {
24798 result.district = component.short_name;
24799 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24800 result.stateOrProvince = component.short_name;
24801 } else if (component.types.indexOf("country") >= 0) {
24802 result.country = component.short_name;
24806 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24807 result.addressLine2 = "";
24811 setZoomLevel: function(zoom)
24813 this.gMapContext.map.setZoom(zoom);
24826 this.fireEvent('show', this);
24837 this.fireEvent('hide', this);
24842 Roo.apply(Roo.bootstrap.LocationPicker, {
24844 OverlayView : function(map, options)
24846 options = options || {};
24860 * @class Roo.bootstrap.Alert
24861 * @extends Roo.bootstrap.Component
24862 * Bootstrap Alert class
24863 * @cfg {String} title The title of alert
24864 * @cfg {String} html The content of alert
24865 * @cfg {String} weight ( success | info | warning | danger )
24866 * @cfg {String} faicon font-awesomeicon
24869 * Create a new alert
24870 * @param {Object} config The config object
24874 Roo.bootstrap.Alert = function(config){
24875 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24879 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
24886 getAutoCreate : function()
24895 cls : 'roo-alert-icon'
24900 cls : 'roo-alert-title',
24905 cls : 'roo-alert-text',
24912 cfg.cn[0].cls += ' fa ' + this.faicon;
24916 cfg.cls += ' alert-' + this.weight;
24922 initEvents: function()
24924 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24927 setTitle : function(str)
24929 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24932 setText : function(str)
24934 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24937 setWeight : function(weight)
24940 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24943 this.weight = weight;
24945 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24948 setIcon : function(icon)
24951 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24954 this.faicon = icon;
24956 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24977 * @class Roo.bootstrap.UploadCropbox
24978 * @extends Roo.bootstrap.Component
24979 * Bootstrap UploadCropbox class
24980 * @cfg {String} emptyText show when image has been loaded
24981 * @cfg {String} rotateNotify show when image too small to rotate
24982 * @cfg {Number} errorTimeout default 3000
24983 * @cfg {Number} minWidth default 300
24984 * @cfg {Number} minHeight default 300
24985 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24986 * @cfg {Boolean} isDocument (true|false) default false
24987 * @cfg {String} url action url
24988 * @cfg {String} paramName default 'imageUpload'
24989 * @cfg {String} method default POST
24990 * @cfg {Boolean} loadMask (true|false) default true
24991 * @cfg {Boolean} loadingText default 'Loading...'
24994 * Create a new UploadCropbox
24995 * @param {Object} config The config object
24998 Roo.bootstrap.UploadCropbox = function(config){
24999 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25003 * @event beforeselectfile
25004 * Fire before select file
25005 * @param {Roo.bootstrap.UploadCropbox} this
25007 "beforeselectfile" : true,
25010 * Fire after initEvent
25011 * @param {Roo.bootstrap.UploadCropbox} this
25016 * Fire after initEvent
25017 * @param {Roo.bootstrap.UploadCropbox} this
25018 * @param {String} data
25023 * Fire when preparing the file data
25024 * @param {Roo.bootstrap.UploadCropbox} this
25025 * @param {Object} file
25030 * Fire when get exception
25031 * @param {Roo.bootstrap.UploadCropbox} this
25032 * @param {XMLHttpRequest} xhr
25034 "exception" : true,
25036 * @event beforeloadcanvas
25037 * Fire before load the canvas
25038 * @param {Roo.bootstrap.UploadCropbox} this
25039 * @param {String} src
25041 "beforeloadcanvas" : true,
25044 * Fire when trash image
25045 * @param {Roo.bootstrap.UploadCropbox} this
25050 * Fire when download the image
25051 * @param {Roo.bootstrap.UploadCropbox} this
25055 * @event footerbuttonclick
25056 * Fire when footerbuttonclick
25057 * @param {Roo.bootstrap.UploadCropbox} this
25058 * @param {String} type
25060 "footerbuttonclick" : true,
25064 * @param {Roo.bootstrap.UploadCropbox} this
25069 * Fire when rotate the image
25070 * @param {Roo.bootstrap.UploadCropbox} this
25071 * @param {String} pos
25076 * Fire when inspect the file
25077 * @param {Roo.bootstrap.UploadCropbox} this
25078 * @param {Object} file
25083 * Fire when xhr upload the file
25084 * @param {Roo.bootstrap.UploadCropbox} this
25085 * @param {Object} data
25090 * Fire when arrange the file data
25091 * @param {Roo.bootstrap.UploadCropbox} this
25092 * @param {Object} formData
25097 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25100 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25102 emptyText : 'Click to upload image',
25103 rotateNotify : 'Image is too small to rotate',
25104 errorTimeout : 3000,
25118 cropType : 'image/jpeg',
25120 canvasLoaded : false,
25121 isDocument : false,
25123 paramName : 'imageUpload',
25125 loadingText : 'Loading...',
25128 getAutoCreate : function()
25132 cls : 'roo-upload-cropbox',
25136 cls : 'roo-upload-cropbox-selector',
25141 cls : 'roo-upload-cropbox-body',
25142 style : 'cursor:pointer',
25146 cls : 'roo-upload-cropbox-preview'
25150 cls : 'roo-upload-cropbox-thumb'
25154 cls : 'roo-upload-cropbox-empty-notify',
25155 html : this.emptyText
25159 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25160 html : this.rotateNotify
25166 cls : 'roo-upload-cropbox-footer',
25169 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25179 onRender : function(ct, position)
25181 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25183 if (this.buttons.length) {
25185 Roo.each(this.buttons, function(bb) {
25187 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25189 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25195 this.maskEl = this.el;
25199 initEvents : function()
25201 this.urlAPI = (window.createObjectURL && window) ||
25202 (window.URL && URL.revokeObjectURL && URL) ||
25203 (window.webkitURL && webkitURL);
25205 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25206 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25208 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25209 this.selectorEl.hide();
25211 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25212 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25214 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25215 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25216 this.thumbEl.hide();
25218 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25219 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25221 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25222 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25223 this.errorEl.hide();
25225 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25226 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25227 this.footerEl.hide();
25229 this.setThumbBoxSize();
25235 this.fireEvent('initial', this);
25242 window.addEventListener("resize", function() { _this.resize(); } );
25244 this.bodyEl.on('click', this.beforeSelectFile, this);
25247 this.bodyEl.on('touchstart', this.onTouchStart, this);
25248 this.bodyEl.on('touchmove', this.onTouchMove, this);
25249 this.bodyEl.on('touchend', this.onTouchEnd, this);
25253 this.bodyEl.on('mousedown', this.onMouseDown, this);
25254 this.bodyEl.on('mousemove', this.onMouseMove, this);
25255 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25256 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25257 Roo.get(document).on('mouseup', this.onMouseUp, this);
25260 this.selectorEl.on('change', this.onFileSelected, this);
25266 this.baseScale = 1;
25268 this.baseRotate = 1;
25269 this.dragable = false;
25270 this.pinching = false;
25273 this.cropData = false;
25274 this.notifyEl.dom.innerHTML = this.emptyText;
25276 this.selectorEl.dom.value = '';
25280 resize : function()
25282 if(this.fireEvent('resize', this) != false){
25283 this.setThumbBoxPosition();
25284 this.setCanvasPosition();
25288 onFooterButtonClick : function(e, el, o, type)
25291 case 'rotate-left' :
25292 this.onRotateLeft(e);
25294 case 'rotate-right' :
25295 this.onRotateRight(e);
25298 this.beforeSelectFile(e);
25313 this.fireEvent('footerbuttonclick', this, type);
25316 beforeSelectFile : function(e)
25318 e.preventDefault();
25320 if(this.fireEvent('beforeselectfile', this) != false){
25321 this.selectorEl.dom.click();
25325 onFileSelected : function(e)
25327 e.preventDefault();
25329 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25333 var file = this.selectorEl.dom.files[0];
25335 if(this.fireEvent('inspect', this, file) != false){
25336 this.prepare(file);
25341 trash : function(e)
25343 this.fireEvent('trash', this);
25346 download : function(e)
25348 this.fireEvent('download', this);
25351 loadCanvas : function(src)
25353 if(this.fireEvent('beforeloadcanvas', this, src) != false){
25357 this.imageEl = document.createElement('img');
25361 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25363 this.imageEl.src = src;
25367 onLoadCanvas : function()
25369 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25370 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25372 this.bodyEl.un('click', this.beforeSelectFile, this);
25374 this.notifyEl.hide();
25375 this.thumbEl.show();
25376 this.footerEl.show();
25378 this.baseRotateLevel();
25380 if(this.isDocument){
25381 this.setThumbBoxSize();
25384 this.setThumbBoxPosition();
25386 this.baseScaleLevel();
25392 this.canvasLoaded = true;
25395 this.maskEl.unmask();
25400 setCanvasPosition : function()
25402 if(!this.canvasEl){
25406 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25407 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25409 this.previewEl.setLeft(pw);
25410 this.previewEl.setTop(ph);
25414 onMouseDown : function(e)
25418 this.dragable = true;
25419 this.pinching = false;
25421 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25422 this.dragable = false;
25426 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25427 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25431 onMouseMove : function(e)
25435 if(!this.canvasLoaded){
25439 if (!this.dragable){
25443 var minX = Math.ceil(this.thumbEl.getLeft(true));
25444 var minY = Math.ceil(this.thumbEl.getTop(true));
25446 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25447 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25449 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25450 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25452 x = x - this.mouseX;
25453 y = y - this.mouseY;
25455 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25456 var bgY = Math.ceil(y + this.previewEl.getTop(true));
25458 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25459 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25461 this.previewEl.setLeft(bgX);
25462 this.previewEl.setTop(bgY);
25464 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25465 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25468 onMouseUp : function(e)
25472 this.dragable = false;
25475 onMouseWheel : function(e)
25479 this.startScale = this.scale;
25481 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25483 if(!this.zoomable()){
25484 this.scale = this.startScale;
25493 zoomable : function()
25495 var minScale = this.thumbEl.getWidth() / this.minWidth;
25497 if(this.minWidth < this.minHeight){
25498 minScale = this.thumbEl.getHeight() / this.minHeight;
25501 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25502 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25506 (this.rotate == 0 || this.rotate == 180) &&
25508 width > this.imageEl.OriginWidth ||
25509 height > this.imageEl.OriginHeight ||
25510 (width < this.minWidth && height < this.minHeight)
25518 (this.rotate == 90 || this.rotate == 270) &&
25520 width > this.imageEl.OriginWidth ||
25521 height > this.imageEl.OriginHeight ||
25522 (width < this.minHeight && height < this.minWidth)
25529 !this.isDocument &&
25530 (this.rotate == 0 || this.rotate == 180) &&
25532 width < this.minWidth ||
25533 width > this.imageEl.OriginWidth ||
25534 height < this.minHeight ||
25535 height > this.imageEl.OriginHeight
25542 !this.isDocument &&
25543 (this.rotate == 90 || this.rotate == 270) &&
25545 width < this.minHeight ||
25546 width > this.imageEl.OriginWidth ||
25547 height < this.minWidth ||
25548 height > this.imageEl.OriginHeight
25558 onRotateLeft : function(e)
25560 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25562 var minScale = this.thumbEl.getWidth() / this.minWidth;
25564 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25565 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25567 this.startScale = this.scale;
25569 while (this.getScaleLevel() < minScale){
25571 this.scale = this.scale + 1;
25573 if(!this.zoomable()){
25578 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25579 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25584 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25591 this.scale = this.startScale;
25593 this.onRotateFail();
25598 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25600 if(this.isDocument){
25601 this.setThumbBoxSize();
25602 this.setThumbBoxPosition();
25603 this.setCanvasPosition();
25608 this.fireEvent('rotate', this, 'left');
25612 onRotateRight : function(e)
25614 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25616 var minScale = this.thumbEl.getWidth() / this.minWidth;
25618 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25619 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25621 this.startScale = this.scale;
25623 while (this.getScaleLevel() < minScale){
25625 this.scale = this.scale + 1;
25627 if(!this.zoomable()){
25632 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25633 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25638 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25645 this.scale = this.startScale;
25647 this.onRotateFail();
25652 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25654 if(this.isDocument){
25655 this.setThumbBoxSize();
25656 this.setThumbBoxPosition();
25657 this.setCanvasPosition();
25662 this.fireEvent('rotate', this, 'right');
25665 onRotateFail : function()
25667 this.errorEl.show(true);
25671 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25676 this.previewEl.dom.innerHTML = '';
25678 var canvasEl = document.createElement("canvas");
25680 var contextEl = canvasEl.getContext("2d");
25682 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25683 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25684 var center = this.imageEl.OriginWidth / 2;
25686 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25687 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25688 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25689 center = this.imageEl.OriginHeight / 2;
25692 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25694 contextEl.translate(center, center);
25695 contextEl.rotate(this.rotate * Math.PI / 180);
25697 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25699 this.canvasEl = document.createElement("canvas");
25701 this.contextEl = this.canvasEl.getContext("2d");
25703 switch (this.rotate) {
25706 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25707 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25709 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25714 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25715 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25717 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25718 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);
25722 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25727 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25728 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25730 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25731 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);
25735 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);
25740 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25741 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25743 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25744 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25748 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);
25755 this.previewEl.appendChild(this.canvasEl);
25757 this.setCanvasPosition();
25762 if(!this.canvasLoaded){
25766 var imageCanvas = document.createElement("canvas");
25768 var imageContext = imageCanvas.getContext("2d");
25770 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25771 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25773 var center = imageCanvas.width / 2;
25775 imageContext.translate(center, center);
25777 imageContext.rotate(this.rotate * Math.PI / 180);
25779 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25781 var canvas = document.createElement("canvas");
25783 var context = canvas.getContext("2d");
25785 canvas.width = this.minWidth;
25786 canvas.height = this.minHeight;
25788 switch (this.rotate) {
25791 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25792 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25794 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25795 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25797 var targetWidth = this.minWidth - 2 * x;
25798 var targetHeight = this.minHeight - 2 * y;
25802 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25803 scale = targetWidth / width;
25806 if(x > 0 && y == 0){
25807 scale = targetHeight / height;
25810 if(x > 0 && y > 0){
25811 scale = targetWidth / width;
25813 if(width < height){
25814 scale = targetHeight / height;
25818 context.scale(scale, scale);
25820 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25821 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25823 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25824 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25826 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25831 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25832 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25834 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25835 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25837 var targetWidth = this.minWidth - 2 * x;
25838 var targetHeight = this.minHeight - 2 * y;
25842 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25843 scale = targetWidth / width;
25846 if(x > 0 && y == 0){
25847 scale = targetHeight / height;
25850 if(x > 0 && y > 0){
25851 scale = targetWidth / width;
25853 if(width < height){
25854 scale = targetHeight / height;
25858 context.scale(scale, scale);
25860 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25861 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25863 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25864 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25866 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25868 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25879 var targetWidth = this.minWidth - 2 * x;
25880 var targetHeight = this.minHeight - 2 * y;
25884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25885 scale = targetWidth / width;
25888 if(x > 0 && y == 0){
25889 scale = targetHeight / height;
25892 if(x > 0 && y > 0){
25893 scale = targetWidth / width;
25895 if(width < height){
25896 scale = targetHeight / height;
25900 context.scale(scale, scale);
25902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25908 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25909 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25911 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25916 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25917 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25919 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25920 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25922 var targetWidth = this.minWidth - 2 * x;
25923 var targetHeight = this.minHeight - 2 * y;
25927 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25928 scale = targetWidth / width;
25931 if(x > 0 && y == 0){
25932 scale = targetHeight / height;
25935 if(x > 0 && y > 0){
25936 scale = targetWidth / width;
25938 if(width < height){
25939 scale = targetHeight / height;
25943 context.scale(scale, scale);
25945 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25946 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25948 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25949 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25951 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25953 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25960 this.cropData = canvas.toDataURL(this.cropType);
25962 if(this.fireEvent('crop', this, this.cropData) !== false){
25963 this.process(this.file, this.cropData);
25970 setThumbBoxSize : function()
25974 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25975 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25976 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25978 this.minWidth = width;
25979 this.minHeight = height;
25981 if(this.rotate == 90 || this.rotate == 270){
25982 this.minWidth = height;
25983 this.minHeight = width;
25988 width = Math.ceil(this.minWidth * height / this.minHeight);
25990 if(this.minWidth > this.minHeight){
25992 height = Math.ceil(this.minHeight * width / this.minWidth);
25995 this.thumbEl.setStyle({
25996 width : width + 'px',
25997 height : height + 'px'
26004 setThumbBoxPosition : function()
26006 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26007 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26009 this.thumbEl.setLeft(x);
26010 this.thumbEl.setTop(y);
26014 baseRotateLevel : function()
26016 this.baseRotate = 1;
26019 typeof(this.exif) != 'undefined' &&
26020 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26021 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26023 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26026 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26030 baseScaleLevel : function()
26034 if(this.isDocument){
26036 if(this.baseRotate == 6 || this.baseRotate == 8){
26038 height = this.thumbEl.getHeight();
26039 this.baseScale = height / this.imageEl.OriginWidth;
26041 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26042 width = this.thumbEl.getWidth();
26043 this.baseScale = width / this.imageEl.OriginHeight;
26049 height = this.thumbEl.getHeight();
26050 this.baseScale = height / this.imageEl.OriginHeight;
26052 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26053 width = this.thumbEl.getWidth();
26054 this.baseScale = width / this.imageEl.OriginWidth;
26060 if(this.baseRotate == 6 || this.baseRotate == 8){
26062 width = this.thumbEl.getHeight();
26063 this.baseScale = width / this.imageEl.OriginHeight;
26065 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26066 height = this.thumbEl.getWidth();
26067 this.baseScale = height / this.imageEl.OriginHeight;
26070 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26071 height = this.thumbEl.getWidth();
26072 this.baseScale = height / this.imageEl.OriginHeight;
26074 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26075 width = this.thumbEl.getHeight();
26076 this.baseScale = width / this.imageEl.OriginWidth;
26083 width = this.thumbEl.getWidth();
26084 this.baseScale = width / this.imageEl.OriginWidth;
26086 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26087 height = this.thumbEl.getHeight();
26088 this.baseScale = height / this.imageEl.OriginHeight;
26091 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26093 height = this.thumbEl.getHeight();
26094 this.baseScale = height / this.imageEl.OriginHeight;
26096 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26097 width = this.thumbEl.getWidth();
26098 this.baseScale = width / this.imageEl.OriginWidth;
26106 getScaleLevel : function()
26108 return this.baseScale * Math.pow(1.1, this.scale);
26111 onTouchStart : function(e)
26113 if(!this.canvasLoaded){
26114 this.beforeSelectFile(e);
26118 var touches = e.browserEvent.touches;
26124 if(touches.length == 1){
26125 this.onMouseDown(e);
26129 if(touches.length != 2){
26135 for(var i = 0, finger; finger = touches[i]; i++){
26136 coords.push(finger.pageX, finger.pageY);
26139 var x = Math.pow(coords[0] - coords[2], 2);
26140 var y = Math.pow(coords[1] - coords[3], 2);
26142 this.startDistance = Math.sqrt(x + y);
26144 this.startScale = this.scale;
26146 this.pinching = true;
26147 this.dragable = false;
26151 onTouchMove : function(e)
26153 if(!this.pinching && !this.dragable){
26157 var touches = e.browserEvent.touches;
26164 this.onMouseMove(e);
26170 for(var i = 0, finger; finger = touches[i]; i++){
26171 coords.push(finger.pageX, finger.pageY);
26174 var x = Math.pow(coords[0] - coords[2], 2);
26175 var y = Math.pow(coords[1] - coords[3], 2);
26177 this.endDistance = Math.sqrt(x + y);
26179 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26181 if(!this.zoomable()){
26182 this.scale = this.startScale;
26190 onTouchEnd : function(e)
26192 this.pinching = false;
26193 this.dragable = false;
26197 process : function(file, crop)
26200 this.maskEl.mask(this.loadingText);
26203 this.xhr = new XMLHttpRequest();
26205 file.xhr = this.xhr;
26207 this.xhr.open(this.method, this.url, true);
26210 "Accept": "application/json",
26211 "Cache-Control": "no-cache",
26212 "X-Requested-With": "XMLHttpRequest"
26215 for (var headerName in headers) {
26216 var headerValue = headers[headerName];
26218 this.xhr.setRequestHeader(headerName, headerValue);
26224 this.xhr.onload = function()
26226 _this.xhrOnLoad(_this.xhr);
26229 this.xhr.onerror = function()
26231 _this.xhrOnError(_this.xhr);
26234 var formData = new FormData();
26236 formData.append('returnHTML', 'NO');
26239 formData.append('crop', crop);
26242 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26243 formData.append(this.paramName, file, file.name);
26246 if(typeof(file.filename) != 'undefined'){
26247 formData.append('filename', file.filename);
26250 if(typeof(file.mimetype) != 'undefined'){
26251 formData.append('mimetype', file.mimetype);
26254 if(this.fireEvent('arrange', this, formData) != false){
26255 this.xhr.send(formData);
26259 xhrOnLoad : function(xhr)
26262 this.maskEl.unmask();
26265 if (xhr.readyState !== 4) {
26266 this.fireEvent('exception', this, xhr);
26270 var response = Roo.decode(xhr.responseText);
26272 if(!response.success){
26273 this.fireEvent('exception', this, xhr);
26277 var response = Roo.decode(xhr.responseText);
26279 this.fireEvent('upload', this, response);
26283 xhrOnError : function()
26286 this.maskEl.unmask();
26289 Roo.log('xhr on error');
26291 var response = Roo.decode(xhr.responseText);
26297 prepare : function(file)
26300 this.maskEl.mask(this.loadingText);
26306 if(typeof(file) === 'string'){
26307 this.loadCanvas(file);
26311 if(!file || !this.urlAPI){
26316 this.cropType = file.type;
26320 if(this.fireEvent('prepare', this, this.file) != false){
26322 var reader = new FileReader();
26324 reader.onload = function (e) {
26325 if (e.target.error) {
26326 Roo.log(e.target.error);
26330 var buffer = e.target.result,
26331 dataView = new DataView(buffer),
26333 maxOffset = dataView.byteLength - 4,
26337 if (dataView.getUint16(0) === 0xffd8) {
26338 while (offset < maxOffset) {
26339 markerBytes = dataView.getUint16(offset);
26341 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26342 markerLength = dataView.getUint16(offset + 2) + 2;
26343 if (offset + markerLength > dataView.byteLength) {
26344 Roo.log('Invalid meta data: Invalid segment size.');
26348 if(markerBytes == 0xffe1){
26349 _this.parseExifData(
26356 offset += markerLength;
26366 var url = _this.urlAPI.createObjectURL(_this.file);
26368 _this.loadCanvas(url);
26373 reader.readAsArrayBuffer(this.file);
26379 parseExifData : function(dataView, offset, length)
26381 var tiffOffset = offset + 10,
26385 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26386 // No Exif data, might be XMP data instead
26390 // Check for the ASCII code for "Exif" (0x45786966):
26391 if (dataView.getUint32(offset + 4) !== 0x45786966) {
26392 // No Exif data, might be XMP data instead
26395 if (tiffOffset + 8 > dataView.byteLength) {
26396 Roo.log('Invalid Exif data: Invalid segment size.');
26399 // Check for the two null bytes:
26400 if (dataView.getUint16(offset + 8) !== 0x0000) {
26401 Roo.log('Invalid Exif data: Missing byte alignment offset.');
26404 // Check the byte alignment:
26405 switch (dataView.getUint16(tiffOffset)) {
26407 littleEndian = true;
26410 littleEndian = false;
26413 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26416 // Check for the TIFF tag marker (0x002A):
26417 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26418 Roo.log('Invalid Exif data: Missing TIFF marker.');
26421 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26422 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26424 this.parseExifTags(
26427 tiffOffset + dirOffset,
26432 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26437 if (dirOffset + 6 > dataView.byteLength) {
26438 Roo.log('Invalid Exif data: Invalid directory offset.');
26441 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26442 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26443 if (dirEndOffset + 4 > dataView.byteLength) {
26444 Roo.log('Invalid Exif data: Invalid directory size.');
26447 for (i = 0; i < tagsNumber; i += 1) {
26451 dirOffset + 2 + 12 * i, // tag offset
26455 // Return the offset to the next directory:
26456 return dataView.getUint32(dirEndOffset, littleEndian);
26459 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
26461 var tag = dataView.getUint16(offset, littleEndian);
26463 this.exif[tag] = this.getExifValue(
26467 dataView.getUint16(offset + 2, littleEndian), // tag type
26468 dataView.getUint32(offset + 4, littleEndian), // tag length
26473 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26475 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26484 Roo.log('Invalid Exif data: Invalid tag type.');
26488 tagSize = tagType.size * length;
26489 // Determine if the value is contained in the dataOffset bytes,
26490 // or if the value at the dataOffset is a pointer to the actual data:
26491 dataOffset = tagSize > 4 ?
26492 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26493 if (dataOffset + tagSize > dataView.byteLength) {
26494 Roo.log('Invalid Exif data: Invalid data offset.');
26497 if (length === 1) {
26498 return tagType.getValue(dataView, dataOffset, littleEndian);
26501 for (i = 0; i < length; i += 1) {
26502 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26505 if (tagType.ascii) {
26507 // Concatenate the chars:
26508 for (i = 0; i < values.length; i += 1) {
26510 // Ignore the terminating NULL byte(s):
26511 if (c === '\u0000') {
26523 Roo.apply(Roo.bootstrap.UploadCropbox, {
26525 'Orientation': 0x0112
26529 1: 0, //'top-left',
26531 3: 180, //'bottom-right',
26532 // 4: 'bottom-left',
26534 6: 90, //'right-top',
26535 // 7: 'right-bottom',
26536 8: 270 //'left-bottom'
26540 // byte, 8-bit unsigned int:
26542 getValue: function (dataView, dataOffset) {
26543 return dataView.getUint8(dataOffset);
26547 // ascii, 8-bit byte:
26549 getValue: function (dataView, dataOffset) {
26550 return String.fromCharCode(dataView.getUint8(dataOffset));
26555 // short, 16 bit int:
26557 getValue: function (dataView, dataOffset, littleEndian) {
26558 return dataView.getUint16(dataOffset, littleEndian);
26562 // long, 32 bit int:
26564 getValue: function (dataView, dataOffset, littleEndian) {
26565 return dataView.getUint32(dataOffset, littleEndian);
26569 // rational = two long values, first is numerator, second is denominator:
26571 getValue: function (dataView, dataOffset, littleEndian) {
26572 return dataView.getUint32(dataOffset, littleEndian) /
26573 dataView.getUint32(dataOffset + 4, littleEndian);
26577 // slong, 32 bit signed int:
26579 getValue: function (dataView, dataOffset, littleEndian) {
26580 return dataView.getInt32(dataOffset, littleEndian);
26584 // srational, two slongs, first is numerator, second is denominator:
26586 getValue: function (dataView, dataOffset, littleEndian) {
26587 return dataView.getInt32(dataOffset, littleEndian) /
26588 dataView.getInt32(dataOffset + 4, littleEndian);
26598 cls : 'btn-group roo-upload-cropbox-rotate-left',
26599 action : 'rotate-left',
26603 cls : 'btn btn-default',
26604 html : '<i class="fa fa-undo"></i>'
26610 cls : 'btn-group roo-upload-cropbox-picture',
26611 action : 'picture',
26615 cls : 'btn btn-default',
26616 html : '<i class="fa fa-picture-o"></i>'
26622 cls : 'btn-group roo-upload-cropbox-rotate-right',
26623 action : 'rotate-right',
26627 cls : 'btn btn-default',
26628 html : '<i class="fa fa-repeat"></i>'
26636 cls : 'btn-group roo-upload-cropbox-rotate-left',
26637 action : 'rotate-left',
26641 cls : 'btn btn-default',
26642 html : '<i class="fa fa-undo"></i>'
26648 cls : 'btn-group roo-upload-cropbox-download',
26649 action : 'download',
26653 cls : 'btn btn-default',
26654 html : '<i class="fa fa-download"></i>'
26660 cls : 'btn-group roo-upload-cropbox-crop',
26665 cls : 'btn btn-default',
26666 html : '<i class="fa fa-crop"></i>'
26672 cls : 'btn-group roo-upload-cropbox-trash',
26677 cls : 'btn btn-default',
26678 html : '<i class="fa fa-trash"></i>'
26684 cls : 'btn-group roo-upload-cropbox-rotate-right',
26685 action : 'rotate-right',
26689 cls : 'btn btn-default',
26690 html : '<i class="fa fa-repeat"></i>'
26698 cls : 'btn-group roo-upload-cropbox-rotate-left',
26699 action : 'rotate-left',
26703 cls : 'btn btn-default',
26704 html : '<i class="fa fa-undo"></i>'
26710 cls : 'btn-group roo-upload-cropbox-rotate-right',
26711 action : 'rotate-right',
26715 cls : 'btn btn-default',
26716 html : '<i class="fa fa-repeat"></i>'
26729 * @class Roo.bootstrap.DocumentManager
26730 * @extends Roo.bootstrap.Component
26731 * Bootstrap DocumentManager class
26732 * @cfg {String} paramName default 'imageUpload'
26733 * @cfg {String} method default POST
26734 * @cfg {String} url action url
26735 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26736 * @cfg {Boolean} multiple multiple upload default true
26737 * @cfg {Number} thumbSize default 300
26738 * @cfg {String} fieldLabel
26739 * @cfg {Number} labelWidth default 4
26740 * @cfg {String} labelAlign (left|top) default left
26741 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26744 * Create a new DocumentManager
26745 * @param {Object} config The config object
26748 Roo.bootstrap.DocumentManager = function(config){
26749 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26754 * Fire when initial the DocumentManager
26755 * @param {Roo.bootstrap.DocumentManager} this
26760 * inspect selected file
26761 * @param {Roo.bootstrap.DocumentManager} this
26762 * @param {File} file
26767 * Fire when xhr load exception
26768 * @param {Roo.bootstrap.DocumentManager} this
26769 * @param {XMLHttpRequest} xhr
26771 "exception" : true,
26774 * prepare the form data
26775 * @param {Roo.bootstrap.DocumentManager} this
26776 * @param {Object} formData
26781 * Fire when remove the file
26782 * @param {Roo.bootstrap.DocumentManager} this
26783 * @param {Object} file
26788 * Fire after refresh the file
26789 * @param {Roo.bootstrap.DocumentManager} this
26794 * Fire after click the image
26795 * @param {Roo.bootstrap.DocumentManager} this
26796 * @param {Object} file
26801 * Fire when upload a image and editable set to true
26802 * @param {Roo.bootstrap.DocumentManager} this
26803 * @param {Object} file
26807 * @event beforeselectfile
26808 * Fire before select file
26809 * @param {Roo.bootstrap.DocumentManager} this
26811 "beforeselectfile" : true,
26814 * Fire before process file
26815 * @param {Roo.bootstrap.DocumentManager} this
26816 * @param {Object} file
26823 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
26832 paramName : 'imageUpload',
26835 labelAlign : 'left',
26842 getAutoCreate : function()
26844 var managerWidget = {
26846 cls : 'roo-document-manager',
26850 cls : 'roo-document-manager-selector',
26855 cls : 'roo-document-manager-uploader',
26859 cls : 'roo-document-manager-upload-btn',
26860 html : '<i class="fa fa-plus"></i>'
26871 cls : 'column col-md-12',
26876 if(this.fieldLabel.length){
26881 cls : 'column col-md-12',
26882 html : this.fieldLabel
26886 cls : 'column col-md-12',
26891 if(this.labelAlign == 'left'){
26895 cls : 'column col-md-' + this.labelWidth,
26896 html : this.fieldLabel
26900 cls : 'column col-md-' + (12 - this.labelWidth),
26910 cls : 'row clearfix',
26918 initEvents : function()
26920 this.managerEl = this.el.select('.roo-document-manager', true).first();
26921 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26923 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26924 this.selectorEl.hide();
26927 this.selectorEl.attr('multiple', 'multiple');
26930 this.selectorEl.on('change', this.onFileSelected, this);
26932 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26933 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26935 this.uploader.on('click', this.onUploaderClick, this);
26937 this.renderProgressDialog();
26941 window.addEventListener("resize", function() { _this.refresh(); } );
26943 this.fireEvent('initial', this);
26946 renderProgressDialog : function()
26950 this.progressDialog = new Roo.bootstrap.Modal({
26951 cls : 'roo-document-manager-progress-dialog',
26952 allow_close : false,
26962 btnclick : function() {
26963 _this.uploadCancel();
26969 this.progressDialog.render(Roo.get(document.body));
26971 this.progress = new Roo.bootstrap.Progress({
26972 cls : 'roo-document-manager-progress',
26977 this.progress.render(this.progressDialog.getChildContainer());
26979 this.progressBar = new Roo.bootstrap.ProgressBar({
26980 cls : 'roo-document-manager-progress-bar',
26983 aria_valuemax : 12,
26987 this.progressBar.render(this.progress.getChildContainer());
26990 onUploaderClick : function(e)
26992 e.preventDefault();
26994 if(this.fireEvent('beforeselectfile', this) != false){
26995 this.selectorEl.dom.click();
27000 onFileSelected : function(e)
27002 e.preventDefault();
27004 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27008 Roo.each(this.selectorEl.dom.files, function(file){
27009 if(this.fireEvent('inspect', this, file) != false){
27010 this.files.push(file);
27020 this.selectorEl.dom.value = '';
27022 if(!this.files.length){
27026 if(this.boxes > 0 && this.files.length > this.boxes){
27027 this.files = this.files.slice(0, this.boxes);
27030 this.uploader.show();
27032 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27033 this.uploader.hide();
27042 Roo.each(this.files, function(file){
27044 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27045 var f = this.renderPreview(file);
27050 if(file.type.indexOf('image') != -1){
27051 this.delegates.push(
27053 _this.process(file);
27054 }).createDelegate(this)
27062 _this.process(file);
27063 }).createDelegate(this)
27068 this.files = files;
27070 this.delegates = this.delegates.concat(docs);
27072 if(!this.delegates.length){
27077 this.progressBar.aria_valuemax = this.delegates.length;
27084 arrange : function()
27086 if(!this.delegates.length){
27087 this.progressDialog.hide();
27092 var delegate = this.delegates.shift();
27094 this.progressDialog.show();
27096 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27098 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27103 refresh : function()
27105 this.uploader.show();
27107 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27108 this.uploader.hide();
27111 Roo.isTouch ? this.closable(false) : this.closable(true);
27113 this.fireEvent('refresh', this);
27116 onRemove : function(e, el, o)
27118 e.preventDefault();
27120 this.fireEvent('remove', this, o);
27124 remove : function(o)
27128 Roo.each(this.files, function(file){
27129 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27138 this.files = files;
27145 Roo.each(this.files, function(file){
27150 file.target.remove();
27159 onClick : function(e, el, o)
27161 e.preventDefault();
27163 this.fireEvent('click', this, o);
27167 closable : function(closable)
27169 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27171 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27183 xhrOnLoad : function(xhr)
27185 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27189 if (xhr.readyState !== 4) {
27191 this.fireEvent('exception', this, xhr);
27195 var response = Roo.decode(xhr.responseText);
27197 if(!response.success){
27199 this.fireEvent('exception', this, xhr);
27203 var file = this.renderPreview(response.data);
27205 this.files.push(file);
27211 xhrOnError : function(xhr)
27213 Roo.log('xhr on error');
27215 var response = Roo.decode(xhr.responseText);
27222 process : function(file)
27224 if(this.fireEvent('process', this, file) !== false){
27225 if(this.editable && file.type.indexOf('image') != -1){
27226 this.fireEvent('edit', this, file);
27230 this.uploadStart(file, false);
27237 uploadStart : function(file, crop)
27239 this.xhr = new XMLHttpRequest();
27241 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27246 file.xhr = this.xhr;
27248 this.managerEl.createChild({
27250 cls : 'roo-document-manager-loading',
27254 tooltip : file.name,
27255 cls : 'roo-document-manager-thumb',
27256 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27262 this.xhr.open(this.method, this.url, true);
27265 "Accept": "application/json",
27266 "Cache-Control": "no-cache",
27267 "X-Requested-With": "XMLHttpRequest"
27270 for (var headerName in headers) {
27271 var headerValue = headers[headerName];
27273 this.xhr.setRequestHeader(headerName, headerValue);
27279 this.xhr.onload = function()
27281 _this.xhrOnLoad(_this.xhr);
27284 this.xhr.onerror = function()
27286 _this.xhrOnError(_this.xhr);
27289 var formData = new FormData();
27291 formData.append('returnHTML', 'NO');
27294 formData.append('crop', crop);
27297 formData.append(this.paramName, file, file.name);
27299 if(this.fireEvent('prepare', this, formData) != false){
27300 this.xhr.send(formData);
27304 uploadCancel : function()
27311 this.delegates = [];
27313 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27320 renderPreview : function(file)
27322 if(typeof(file.target) != 'undefined' && file.target){
27326 var previewEl = this.managerEl.createChild({
27328 cls : 'roo-document-manager-preview',
27332 tooltip : file.filename,
27333 cls : 'roo-document-manager-thumb',
27334 html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27339 html : '<i class="fa fa-times-circle"></i>'
27344 var close = previewEl.select('button.close', true).first();
27346 close.on('click', this.onRemove, this, file);
27348 file.target = previewEl;
27350 var image = previewEl.select('img', true).first();
27354 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27356 image.on('click', this.onClick, this, file);
27362 onPreviewLoad : function(file, image)
27364 if(typeof(file.target) == 'undefined' || !file.target){
27368 var width = image.dom.naturalWidth || image.dom.width;
27369 var height = image.dom.naturalHeight || image.dom.height;
27371 if(width > height){
27372 file.target.addClass('wide');
27376 file.target.addClass('tall');
27381 uploadFromSource : function(file, crop)
27383 this.xhr = new XMLHttpRequest();
27385 this.managerEl.createChild({
27387 cls : 'roo-document-manager-loading',
27391 tooltip : file.name,
27392 cls : 'roo-document-manager-thumb',
27393 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27399 this.xhr.open(this.method, this.url, true);
27402 "Accept": "application/json",
27403 "Cache-Control": "no-cache",
27404 "X-Requested-With": "XMLHttpRequest"
27407 for (var headerName in headers) {
27408 var headerValue = headers[headerName];
27410 this.xhr.setRequestHeader(headerName, headerValue);
27416 this.xhr.onload = function()
27418 _this.xhrOnLoad(_this.xhr);
27421 this.xhr.onerror = function()
27423 _this.xhrOnError(_this.xhr);
27426 var formData = new FormData();
27428 formData.append('returnHTML', 'NO');
27430 formData.append('crop', crop);
27432 if(typeof(file.filename) != 'undefined'){
27433 formData.append('filename', file.filename);
27436 if(typeof(file.mimetype) != 'undefined'){
27437 formData.append('mimetype', file.mimetype);
27440 if(this.fireEvent('prepare', this, formData) != false){
27441 this.xhr.send(formData);
27451 * @class Roo.bootstrap.DocumentViewer
27452 * @extends Roo.bootstrap.Component
27453 * Bootstrap DocumentViewer class
27456 * Create a new DocumentViewer
27457 * @param {Object} config The config object
27460 Roo.bootstrap.DocumentViewer = function(config){
27461 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27466 * Fire after initEvent
27467 * @param {Roo.bootstrap.DocumentViewer} this
27473 * @param {Roo.bootstrap.DocumentViewer} this
27478 * Fire after trash button
27479 * @param {Roo.bootstrap.DocumentViewer} this
27486 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
27488 getAutoCreate : function()
27492 cls : 'roo-document-viewer',
27496 cls : 'roo-document-viewer-body',
27500 cls : 'roo-document-viewer-thumb',
27504 cls : 'roo-document-viewer-image'
27512 cls : 'roo-document-viewer-footer',
27515 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27523 cls : 'btn btn-default roo-document-viewer-trash',
27524 html : '<i class="fa fa-trash"></i>'
27537 initEvents : function()
27540 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27541 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27543 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27544 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27546 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27547 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27549 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27550 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27552 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27553 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27555 this.bodyEl.on('click', this.onClick, this);
27557 this.trashBtn.on('click', this.onTrash, this);
27561 initial : function()
27563 // this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27566 this.fireEvent('initial', this);
27570 onClick : function(e)
27572 e.preventDefault();
27574 this.fireEvent('click', this);
27577 onTrash : function(e)
27579 e.preventDefault();
27581 this.fireEvent('trash', this);
27593 * @class Roo.bootstrap.NavProgressBar
27594 * @extends Roo.bootstrap.Component
27595 * Bootstrap NavProgressBar class
27598 * Create a new nav progress bar
27599 * @param {Object} config The config object
27602 Roo.bootstrap.NavProgressBar = function(config){
27603 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27605 this.bullets = this.bullets || [];
27607 // Roo.bootstrap.NavProgressBar.register(this);
27611 * Fires when the active item changes
27612 * @param {Roo.bootstrap.NavProgressBar} this
27613 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27614 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
27621 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
27626 getAutoCreate : function()
27628 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27632 cls : 'roo-navigation-bar-group',
27636 cls : 'roo-navigation-top-bar'
27640 cls : 'roo-navigation-bullets-bar',
27644 cls : 'roo-navigation-bar'
27651 cls : 'roo-navigation-bottom-bar'
27661 initEvents: function()
27666 onRender : function(ct, position)
27668 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27670 if(this.bullets.length){
27671 Roo.each(this.bullets, function(b){
27680 addItem : function(cfg)
27682 var item = new Roo.bootstrap.NavProgressItem(cfg);
27684 item.parentId = this.id;
27685 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27688 var top = new Roo.bootstrap.Element({
27690 cls : 'roo-navigation-bar-text'
27693 var bottom = new Roo.bootstrap.Element({
27695 cls : 'roo-navigation-bar-text'
27698 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27699 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27701 var topText = new Roo.bootstrap.Element({
27703 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27706 var bottomText = new Roo.bootstrap.Element({
27708 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27711 topText.onRender(top.el, null);
27712 bottomText.onRender(bottom.el, null);
27715 item.bottomEl = bottom;
27718 this.barItems.push(item);
27723 getActive : function()
27725 var active = false;
27727 Roo.each(this.barItems, function(v){
27729 if (!v.isActive()) {
27741 setActiveItem : function(item)
27745 Roo.each(this.barItems, function(v){
27746 if (v.rid == item.rid) {
27750 if (v.isActive()) {
27751 v.setActive(false);
27756 item.setActive(true);
27758 this.fireEvent('changed', this, item, prev);
27761 getBarItem: function(rid)
27765 Roo.each(this.barItems, function(e) {
27766 if (e.rid != rid) {
27777 indexOfItem : function(item)
27781 Roo.each(this.barItems, function(v, i){
27783 if (v.rid != item.rid) {
27794 setActiveNext : function()
27796 var i = this.indexOfItem(this.getActive());
27798 if (i > this.barItems.length) {
27802 this.setActiveItem(this.barItems[i+1]);
27805 setActivePrev : function()
27807 var i = this.indexOfItem(this.getActive());
27813 this.setActiveItem(this.barItems[i-1]);
27816 format : function()
27818 if(!this.barItems.length){
27822 var width = 100 / this.barItems.length;
27824 Roo.each(this.barItems, function(i){
27825 i.el.setStyle('width', width + '%');
27826 i.topEl.el.setStyle('width', width + '%');
27827 i.bottomEl.el.setStyle('width', width + '%');
27836 * Nav Progress Item
27841 * @class Roo.bootstrap.NavProgressItem
27842 * @extends Roo.bootstrap.Component
27843 * Bootstrap NavProgressItem class
27844 * @cfg {String} rid the reference id
27845 * @cfg {Boolean} active (true|false) Is item active default false
27846 * @cfg {Boolean} disabled (true|false) Is item active default false
27847 * @cfg {String} html
27848 * @cfg {String} position (top|bottom) text position default bottom
27849 * @cfg {String} icon show icon instead of number
27852 * Create a new NavProgressItem
27853 * @param {Object} config The config object
27855 Roo.bootstrap.NavProgressItem = function(config){
27856 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27861 * The raw click event for the entire grid.
27862 * @param {Roo.bootstrap.NavProgressItem} this
27863 * @param {Roo.EventObject} e
27870 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
27876 position : 'bottom',
27879 getAutoCreate : function()
27881 var iconCls = 'roo-navigation-bar-item-icon';
27883 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27887 cls: 'roo-navigation-bar-item',
27897 cfg.cls += ' active';
27900 cfg.cls += ' disabled';
27906 disable : function()
27908 this.setDisabled(true);
27911 enable : function()
27913 this.setDisabled(false);
27916 initEvents: function()
27918 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27920 this.iconEl.on('click', this.onClick, this);
27923 onClick : function(e)
27925 e.preventDefault();
27931 if(this.fireEvent('click', this, e) === false){
27935 this.parent().setActiveItem(this);
27938 isActive: function ()
27940 return this.active;
27943 setActive : function(state)
27945 if(this.active == state){
27949 this.active = state;
27952 this.el.addClass('active');
27956 this.el.removeClass('active');
27961 setDisabled : function(state)
27963 if(this.disabled == state){
27967 this.disabled = state;
27970 this.el.addClass('disabled');
27974 this.el.removeClass('disabled');
27977 tooltipEl : function()
27979 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27992 * @class Roo.bootstrap.FieldLabel
27993 * @extends Roo.bootstrap.Component
27994 * Bootstrap FieldLabel class
27995 * @cfg {String} html contents of the element
27996 * @cfg {String} tag tag of the element default label
27997 * @cfg {String} cls class of the element
27998 * @cfg {String} target label target
27999 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28000 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28001 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28002 * @cfg {String} iconTooltip default "This field is required"
28005 * Create a new FieldLabel
28006 * @param {Object} config The config object
28009 Roo.bootstrap.FieldLabel = function(config){
28010 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28015 * Fires after the field has been marked as invalid.
28016 * @param {Roo.form.FieldLabel} this
28017 * @param {String} msg The validation message
28022 * Fires after the field has been validated with no errors.
28023 * @param {Roo.form.FieldLabel} this
28029 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28036 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28037 validClass : 'text-success fa fa-lg fa-check',
28038 iconTooltip : 'This field is required',
28040 getAutoCreate : function(){
28044 cls : 'roo-bootstrap-field-label ' + this.cls,
28050 tooltip : this.iconTooltip
28062 initEvents: function()
28064 Roo.bootstrap.Element.superclass.initEvents.call(this);
28066 this.iconEl = this.el.select('i', true).first();
28068 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28070 Roo.bootstrap.FieldLabel.register(this);
28074 * Mark this field as valid
28076 markValid : function()
28078 this.iconEl.show();
28080 this.iconEl.removeClass(this.invalidClass);
28082 this.iconEl.addClass(this.validClass);
28084 this.fireEvent('valid', this);
28088 * Mark this field as invalid
28089 * @param {String} msg The validation message
28091 markInvalid : function(msg)
28093 this.iconEl.show();
28095 this.iconEl.removeClass(this.validClass);
28097 this.iconEl.addClass(this.invalidClass);
28099 this.fireEvent('invalid', this, msg);
28105 Roo.apply(Roo.bootstrap.FieldLabel, {
28110 * register a FieldLabel Group
28111 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28113 register : function(label)
28115 if(this.groups.hasOwnProperty(label.target)){
28119 this.groups[label.target] = label;
28123 * fetch a FieldLabel Group based on the target
28124 * @param {string} target
28125 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28127 get: function(target) {
28128 if (typeof(this.groups[target]) == 'undefined') {
28132 return this.groups[target] ;
28141 * page DateSplitField.
28147 * @class Roo.bootstrap.DateSplitField
28148 * @extends Roo.bootstrap.Component
28149 * Bootstrap DateSplitField class
28150 * @cfg {string} fieldLabel - the label associated
28151 * @cfg {Number} labelWidth set the width of label (0-12)
28152 * @cfg {String} labelAlign (top|left)
28153 * @cfg {Boolean} dayAllowBlank (true|false) default false
28154 * @cfg {Boolean} monthAllowBlank (true|false) default false
28155 * @cfg {Boolean} yearAllowBlank (true|false) default false
28156 * @cfg {string} dayPlaceholder
28157 * @cfg {string} monthPlaceholder
28158 * @cfg {string} yearPlaceholder
28159 * @cfg {string} dayFormat default 'd'
28160 * @cfg {string} monthFormat default 'm'
28161 * @cfg {string} yearFormat default 'Y'
28165 * Create a new DateSplitField
28166 * @param {Object} config The config object
28169 Roo.bootstrap.DateSplitField = function(config){
28170 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28176 * getting the data of years
28177 * @param {Roo.bootstrap.DateSplitField} this
28178 * @param {Object} years
28183 * getting the data of days
28184 * @param {Roo.bootstrap.DateSplitField} this
28185 * @param {Object} days
28190 * Fires after the field has been marked as invalid.
28191 * @param {Roo.form.Field} this
28192 * @param {String} msg The validation message
28197 * Fires after the field has been validated with no errors.
28198 * @param {Roo.form.Field} this
28204 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
28207 labelAlign : 'top',
28209 dayAllowBlank : false,
28210 monthAllowBlank : false,
28211 yearAllowBlank : false,
28212 dayPlaceholder : '',
28213 monthPlaceholder : '',
28214 yearPlaceholder : '',
28218 isFormField : true,
28220 getAutoCreate : function()
28224 cls : 'row roo-date-split-field-group',
28229 cls : 'form-hidden-field roo-date-split-field-group-value',
28235 if(this.fieldLabel){
28238 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28242 html : this.fieldLabel
28248 Roo.each(['day', 'month', 'year'], function(t){
28251 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28258 inputEl: function ()
28260 return this.el.select('.roo-date-split-field-group-value', true).first();
28263 onRender : function(ct, position)
28267 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28269 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28271 this.dayField = new Roo.bootstrap.ComboBox({
28272 allowBlank : this.dayAllowBlank,
28273 alwaysQuery : true,
28274 displayField : 'value',
28277 forceSelection : true,
28279 placeholder : this.dayPlaceholder,
28280 selectOnFocus : true,
28281 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28282 triggerAction : 'all',
28284 valueField : 'value',
28285 store : new Roo.data.SimpleStore({
28286 data : (function() {
28288 _this.fireEvent('days', _this, days);
28291 fields : [ 'value' ]
28294 select : function (_self, record, index)
28296 _this.setValue(_this.getValue());
28301 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28303 this.monthField = new Roo.bootstrap.MonthField({
28304 after : '<i class=\"fa fa-calendar\"></i>',
28305 allowBlank : this.monthAllowBlank,
28306 placeholder : this.monthPlaceholder,
28309 render : function (_self)
28311 this.el.select('span.input-group-addon', true).first().on('click', function(e){
28312 e.preventDefault();
28316 select : function (_self, oldvalue, newvalue)
28318 _this.setValue(_this.getValue());
28323 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28325 this.yearField = new Roo.bootstrap.ComboBox({
28326 allowBlank : this.yearAllowBlank,
28327 alwaysQuery : true,
28328 displayField : 'value',
28331 forceSelection : true,
28333 placeholder : this.yearPlaceholder,
28334 selectOnFocus : true,
28335 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28336 triggerAction : 'all',
28338 valueField : 'value',
28339 store : new Roo.data.SimpleStore({
28340 data : (function() {
28342 _this.fireEvent('years', _this, years);
28345 fields : [ 'value' ]
28348 select : function (_self, record, index)
28350 _this.setValue(_this.getValue());
28355 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28358 setValue : function(v, format)
28360 this.inputEl.dom.value = v;
28362 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28364 var d = Date.parseDate(v, f);
28371 this.setDay(d.format(this.dayFormat));
28372 this.setMonth(d.format(this.monthFormat));
28373 this.setYear(d.format(this.yearFormat));
28380 setDay : function(v)
28382 this.dayField.setValue(v);
28383 this.inputEl.dom.value = this.getValue();
28388 setMonth : function(v)
28390 this.monthField.setValue(v, true);
28391 this.inputEl.dom.value = this.getValue();
28396 setYear : function(v)
28398 this.yearField.setValue(v);
28399 this.inputEl.dom.value = this.getValue();
28404 getDay : function()
28406 return this.dayField.getValue();
28409 getMonth : function()
28411 return this.monthField.getValue();
28414 getYear : function()
28416 return this.yearField.getValue();
28419 getValue : function()
28421 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28423 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28433 this.inputEl.dom.value = '';
28438 validate : function()
28440 var d = this.dayField.validate();
28441 var m = this.monthField.validate();
28442 var y = this.yearField.validate();
28447 (!this.dayAllowBlank && !d) ||
28448 (!this.monthAllowBlank && !m) ||
28449 (!this.yearAllowBlank && !y)
28454 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28463 this.markInvalid();
28468 markValid : function()
28471 var label = this.el.select('label', true).first();
28472 var icon = this.el.select('i.fa-star', true).first();
28478 this.fireEvent('valid', this);
28482 * Mark this field as invalid
28483 * @param {String} msg The validation message
28485 markInvalid : function(msg)
28488 var label = this.el.select('label', true).first();
28489 var icon = this.el.select('i.fa-star', true).first();
28491 if(label && !icon){
28492 this.el.select('.roo-date-split-field-label', true).createChild({
28494 cls : 'text-danger fa fa-lg fa-star',
28495 tooltip : 'This field is required',
28496 style : 'margin-right:5px;'
28500 this.fireEvent('invalid', this, msg);
28503 clearInvalid : function()
28505 var label = this.el.select('label', true).first();
28506 var icon = this.el.select('i.fa-star', true).first();
28512 this.fireEvent('valid', this);
28515 getName: function()
28525 * http://masonry.desandro.com
28527 * The idea is to render all the bricks based on vertical width...
28529 * The original code extends 'outlayer' - we might need to use that....
28535 * @class Roo.bootstrap.LayoutMasonry
28536 * @extends Roo.bootstrap.Component
28537 * Bootstrap Layout Masonry class
28540 * Create a new Element
28541 * @param {Object} config The config object
28544 Roo.bootstrap.LayoutMasonry = function(config){
28545 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28551 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
28554 * @cfg {Boolean} isLayoutInstant = no animation?
28556 isLayoutInstant : false, // needed?
28559 * @cfg {Number} boxWidth width of the columns
28564 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
28569 * @cfg {Number} padWidth padding below box..
28574 * @cfg {Number} gutter gutter width..
28579 * @cfg {Number} maxCols maximum number of columns
28585 * @cfg {Boolean} isAutoInitial defalut true
28587 isAutoInitial : true,
28592 * @cfg {Boolean} isHorizontal defalut false
28594 isHorizontal : false,
28596 currentSize : null,
28602 bricks: null, //CompositeElement
28606 _isLayoutInited : false,
28608 // isAlternative : false, // only use for vertical layout...
28611 * @cfg {Number} alternativePadWidth padding below box..
28613 alternativePadWidth : 50,
28615 getAutoCreate : function(){
28619 cls: 'blog-masonary-wrapper ' + this.cls,
28621 cls : 'mas-boxes masonary'
28628 getChildContainer: function( )
28630 if (this.boxesEl) {
28631 return this.boxesEl;
28634 this.boxesEl = this.el.select('.mas-boxes').first();
28636 return this.boxesEl;
28640 initEvents : function()
28644 if(this.isAutoInitial){
28645 Roo.log('hook children rendered');
28646 this.on('childrenrendered', function() {
28647 Roo.log('children rendered');
28653 initial : function()
28655 this.currentSize = this.el.getBox(true);
28657 Roo.EventManager.onWindowResize(this.resize, this);
28659 if(!this.isAutoInitial){
28667 //this.layout.defer(500,this);
28671 resize : function()
28675 var cs = this.el.getBox(true);
28677 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28678 Roo.log("no change in with or X");
28682 this.currentSize = cs;
28688 layout : function()
28690 this._resetLayout();
28692 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28694 this.layoutItems( isInstant );
28696 this._isLayoutInited = true;
28700 _resetLayout : function()
28702 if(this.isHorizontal){
28703 this.horizontalMeasureColumns();
28707 this.verticalMeasureColumns();
28711 verticalMeasureColumns : function()
28713 this.getContainerWidth();
28715 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28716 // this.colWidth = Math.floor(this.containerWidth * 0.8);
28720 var boxWidth = this.boxWidth + this.padWidth;
28722 if(this.containerWidth < this.boxWidth){
28723 boxWidth = this.containerWidth
28726 var containerWidth = this.containerWidth;
28728 var cols = Math.floor(containerWidth / boxWidth);
28730 this.cols = Math.max( cols, 1 );
28732 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28734 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28736 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28738 this.colWidth = boxWidth + avail - this.padWidth;
28740 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28741 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
28744 horizontalMeasureColumns : function()
28746 this.getContainerWidth();
28748 var boxWidth = this.boxWidth;
28750 if(this.containerWidth < boxWidth){
28751 boxWidth = this.containerWidth;
28754 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28756 this.el.setHeight(boxWidth);
28760 getContainerWidth : function()
28762 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
28765 layoutItems : function( isInstant )
28767 var items = Roo.apply([], this.bricks);
28769 if(this.isHorizontal){
28770 this._horizontalLayoutItems( items , isInstant );
28774 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28775 // this._verticalAlternativeLayoutItems( items , isInstant );
28779 this._verticalLayoutItems( items , isInstant );
28783 _verticalLayoutItems : function ( items , isInstant)
28785 if ( !items || !items.length ) {
28790 ['xs', 'xs', 'xs', 'tall'],
28791 ['xs', 'xs', 'tall'],
28792 ['xs', 'xs', 'sm'],
28793 ['xs', 'xs', 'xs'],
28799 ['sm', 'xs', 'xs'],
28803 ['tall', 'xs', 'xs', 'xs'],
28804 ['tall', 'xs', 'xs'],
28816 Roo.each(items, function(item, k){
28818 switch (item.size) {
28819 // these layouts take up a full box,
28830 boxes.push([item]);
28853 var filterPattern = function(box, length)
28861 var pattern = box.slice(0, length);
28865 Roo.each(pattern, function(i){
28866 format.push(i.size);
28869 Roo.each(standard, function(s){
28871 if(String(s) != String(format)){
28880 if(!match && length == 1){
28885 filterPattern(box, length - 1);
28889 queue.push(pattern);
28891 box = box.slice(length, box.length);
28893 filterPattern(box, 4);
28899 Roo.each(boxes, function(box, k){
28905 if(box.length == 1){
28910 filterPattern(box, 4);
28914 this._processVerticalLayoutQueue( queue, isInstant );
28918 // _verticalAlternativeLayoutItems : function( items , isInstant )
28920 // if ( !items || !items.length ) {
28924 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
28928 _horizontalLayoutItems : function ( items , isInstant)
28930 if ( !items || !items.length || items.length < 3) {
28936 var eItems = items.slice(0, 3);
28938 items = items.slice(3, items.length);
28941 ['xs', 'xs', 'xs', 'wide'],
28942 ['xs', 'xs', 'wide'],
28943 ['xs', 'xs', 'sm'],
28944 ['xs', 'xs', 'xs'],
28950 ['sm', 'xs', 'xs'],
28954 ['wide', 'xs', 'xs', 'xs'],
28955 ['wide', 'xs', 'xs'],
28968 Roo.each(items, function(item, k){
28970 switch (item.size) {
28981 boxes.push([item]);
29005 var filterPattern = function(box, length)
29013 var pattern = box.slice(0, length);
29017 Roo.each(pattern, function(i){
29018 format.push(i.size);
29021 Roo.each(standard, function(s){
29023 if(String(s) != String(format)){
29032 if(!match && length == 1){
29037 filterPattern(box, length - 1);
29041 queue.push(pattern);
29043 box = box.slice(length, box.length);
29045 filterPattern(box, 4);
29051 Roo.each(boxes, function(box, k){
29057 if(box.length == 1){
29062 filterPattern(box, 4);
29069 var pos = this.el.getBox(true);
29073 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29075 var hit_end = false;
29077 Roo.each(queue, function(box){
29081 Roo.each(box, function(b){
29083 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29093 Roo.each(box, function(b){
29095 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29098 mx = Math.max(mx, b.x);
29102 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29106 Roo.each(box, function(b){
29108 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29122 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29125 /** Sets position of item in DOM
29126 * @param {Element} item
29127 * @param {Number} x - horizontal position
29128 * @param {Number} y - vertical position
29129 * @param {Boolean} isInstant - disables transitions
29131 _processVerticalLayoutQueue : function( queue, isInstant )
29133 var pos = this.el.getBox(true);
29138 for (var i = 0; i < this.cols; i++){
29142 Roo.each(queue, function(box, k){
29144 var col = k % this.cols;
29146 Roo.each(box, function(b,kk){
29148 b.el.position('absolute');
29150 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29151 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29153 if(b.size == 'md-left' || b.size == 'md-right'){
29154 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29155 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29158 b.el.setWidth(width);
29159 b.el.setHeight(height);
29161 b.el.select('iframe',true).setSize(width,height);
29165 for (var i = 0; i < this.cols; i++){
29167 if(maxY[i] < maxY[col]){
29172 col = Math.min(col, i);
29176 x = pos.x + col * (this.colWidth + this.padWidth);
29180 var positions = [];
29182 switch (box.length){
29184 positions = this.getVerticalOneBoxColPositions(x, y, box);
29187 positions = this.getVerticalTwoBoxColPositions(x, y, box);
29190 positions = this.getVerticalThreeBoxColPositions(x, y, box);
29193 positions = this.getVerticalFourBoxColPositions(x, y, box);
29199 Roo.each(box, function(b,kk){
29201 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29203 var sz = b.el.getSize();
29205 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29213 for (var i = 0; i < this.cols; i++){
29214 mY = Math.max(mY, maxY[i]);
29217 this.el.setHeight(mY - pos.y);
29221 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29223 // var pos = this.el.getBox(true);
29226 // var maxX = pos.right;
29228 // var maxHeight = 0;
29230 // Roo.each(items, function(item, k){
29234 // item.el.position('absolute');
29236 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29238 // item.el.setWidth(width);
29240 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29242 // item.el.setHeight(height);
29245 // item.el.setXY([x, y], isInstant ? false : true);
29247 // item.el.setXY([maxX - width, y], isInstant ? false : true);
29250 // y = y + height + this.alternativePadWidth;
29252 // maxHeight = maxHeight + height + this.alternativePadWidth;
29256 // this.el.setHeight(maxHeight);
29260 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29262 var pos = this.el.getBox(true);
29267 var maxX = pos.right;
29269 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29271 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29273 Roo.each(queue, function(box, k){
29275 Roo.each(box, function(b, kk){
29277 b.el.position('absolute');
29279 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29280 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29282 if(b.size == 'md-left' || b.size == 'md-right'){
29283 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29284 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29287 b.el.setWidth(width);
29288 b.el.setHeight(height);
29296 var positions = [];
29298 switch (box.length){
29300 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29303 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29306 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29309 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29315 Roo.each(box, function(b,kk){
29317 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29319 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29327 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29329 Roo.each(eItems, function(b,k){
29331 b.size = (k == 0) ? 'sm' : 'xs';
29332 b.x = (k == 0) ? 2 : 1;
29333 b.y = (k == 0) ? 2 : 1;
29335 b.el.position('absolute');
29337 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29339 b.el.setWidth(width);
29341 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29343 b.el.setHeight(height);
29347 var positions = [];
29350 x : maxX - this.unitWidth * 2 - this.gutter,
29355 x : maxX - this.unitWidth,
29356 y : minY + (this.unitWidth + this.gutter) * 2
29360 x : maxX - this.unitWidth * 3 - this.gutter * 2,
29364 Roo.each(eItems, function(b,k){
29366 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29372 getVerticalOneBoxColPositions : function(x, y, box)
29376 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29378 if(box[0].size == 'md-left'){
29382 if(box[0].size == 'md-right'){
29387 x : x + (this.unitWidth + this.gutter) * rand,
29394 getVerticalTwoBoxColPositions : function(x, y, box)
29398 if(box[0].size == 'xs'){
29402 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29406 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29420 x : x + (this.unitWidth + this.gutter) * 2,
29421 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29428 getVerticalThreeBoxColPositions : function(x, y, box)
29432 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29440 x : x + (this.unitWidth + this.gutter) * 1,
29445 x : x + (this.unitWidth + this.gutter) * 2,
29453 if(box[0].size == 'xs' && box[1].size == 'xs'){
29462 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29466 x : x + (this.unitWidth + this.gutter) * 1,
29480 x : x + (this.unitWidth + this.gutter) * 2,
29485 x : x + (this.unitWidth + this.gutter) * 2,
29486 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29493 getVerticalFourBoxColPositions : function(x, y, box)
29497 if(box[0].size == 'xs'){
29506 y : y + (this.unitHeight + this.gutter) * 1
29511 y : y + (this.unitHeight + this.gutter) * 2
29515 x : x + (this.unitWidth + this.gutter) * 1,
29529 x : x + (this.unitWidth + this.gutter) * 2,
29534 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29535 y : y + (this.unitHeight + this.gutter) * 1
29539 x : x + (this.unitWidth + this.gutter) * 2,
29540 y : y + (this.unitWidth + this.gutter) * 2
29547 getHorizontalOneBoxColPositions : function(maxX, minY, box)
29551 if(box[0].size == 'md-left'){
29553 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29560 if(box[0].size == 'md-right'){
29562 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29563 y : minY + (this.unitWidth + this.gutter) * 1
29569 var rand = Math.floor(Math.random() * (4 - box[0].y));
29572 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29573 y : minY + (this.unitWidth + this.gutter) * rand
29580 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29584 if(box[0].size == 'xs'){
29587 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29592 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29593 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29601 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29606 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29607 y : minY + (this.unitWidth + this.gutter) * 2
29614 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29618 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29621 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29626 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29627 y : minY + (this.unitWidth + this.gutter) * 1
29631 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29632 y : minY + (this.unitWidth + this.gutter) * 2
29639 if(box[0].size == 'xs' && box[1].size == 'xs'){
29642 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29647 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29652 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29653 y : minY + (this.unitWidth + this.gutter) * 1
29661 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29666 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29667 y : minY + (this.unitWidth + this.gutter) * 2
29671 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29672 y : minY + (this.unitWidth + this.gutter) * 2
29679 getHorizontalFourBoxColPositions : function(maxX, minY, box)
29683 if(box[0].size == 'xs'){
29686 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29691 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29696 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),
29701 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29702 y : minY + (this.unitWidth + this.gutter) * 1
29710 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29715 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29716 y : minY + (this.unitWidth + this.gutter) * 2
29720 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29721 y : minY + (this.unitWidth + this.gutter) * 2
29725 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),
29726 y : minY + (this.unitWidth + this.gutter) * 2
29740 * http://masonry.desandro.com
29742 * The idea is to render all the bricks based on vertical width...
29744 * The original code extends 'outlayer' - we might need to use that....
29750 * @class Roo.bootstrap.LayoutMasonryAuto
29751 * @extends Roo.bootstrap.Component
29752 * Bootstrap Layout Masonry class
29755 * Create a new Element
29756 * @param {Object} config The config object
29759 Roo.bootstrap.LayoutMasonryAuto = function(config){
29760 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29763 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
29766 * @cfg {Boolean} isFitWidth - resize the width..
29768 isFitWidth : false, // options..
29770 * @cfg {Boolean} isOriginLeft = left align?
29772 isOriginLeft : true,
29774 * @cfg {Boolean} isOriginTop = top align?
29776 isOriginTop : false,
29778 * @cfg {Boolean} isLayoutInstant = no animation?
29780 isLayoutInstant : false, // needed?
29782 * @cfg {Boolean} isResizingContainer = not sure if this is used..
29784 isResizingContainer : true,
29786 * @cfg {Number} columnWidth width of the columns
29792 * @cfg {Number} maxCols maximum number of columns
29797 * @cfg {Number} padHeight padding below box..
29803 * @cfg {Boolean} isAutoInitial defalut true
29806 isAutoInitial : true,
29812 initialColumnWidth : 0,
29813 currentSize : null,
29815 colYs : null, // array.
29822 bricks: null, //CompositeElement
29823 cols : 0, // array?
29824 // element : null, // wrapped now this.el
29825 _isLayoutInited : null,
29828 getAutoCreate : function(){
29832 cls: 'blog-masonary-wrapper ' + this.cls,
29834 cls : 'mas-boxes masonary'
29841 getChildContainer: function( )
29843 if (this.boxesEl) {
29844 return this.boxesEl;
29847 this.boxesEl = this.el.select('.mas-boxes').first();
29849 return this.boxesEl;
29853 initEvents : function()
29857 if(this.isAutoInitial){
29858 Roo.log('hook children rendered');
29859 this.on('childrenrendered', function() {
29860 Roo.log('children rendered');
29867 initial : function()
29869 this.reloadItems();
29871 this.currentSize = this.el.getBox(true);
29873 /// was window resize... - let's see if this works..
29874 Roo.EventManager.onWindowResize(this.resize, this);
29876 if(!this.isAutoInitial){
29881 this.layout.defer(500,this);
29884 reloadItems: function()
29886 this.bricks = this.el.select('.masonry-brick', true);
29888 this.bricks.each(function(b) {
29889 //Roo.log(b.getSize());
29890 if (!b.attr('originalwidth')) {
29891 b.attr('originalwidth', b.getSize().width);
29896 Roo.log(this.bricks.elements.length);
29899 resize : function()
29902 var cs = this.el.getBox(true);
29904 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29905 Roo.log("no change in with or X");
29908 this.currentSize = cs;
29912 layout : function()
29915 this._resetLayout();
29916 //this._manageStamps();
29918 // don't animate first layout
29919 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29920 this.layoutItems( isInstant );
29922 // flag for initalized
29923 this._isLayoutInited = true;
29926 layoutItems : function( isInstant )
29928 //var items = this._getItemsForLayout( this.items );
29929 // original code supports filtering layout items.. we just ignore it..
29931 this._layoutItems( this.bricks , isInstant );
29933 this._postLayout();
29935 _layoutItems : function ( items , isInstant)
29937 //this.fireEvent( 'layout', this, items );
29940 if ( !items || !items.elements.length ) {
29941 // no items, emit event with empty array
29946 items.each(function(item) {
29947 Roo.log("layout item");
29949 // get x/y object from method
29950 var position = this._getItemLayoutPosition( item );
29952 position.item = item;
29953 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29954 queue.push( position );
29957 this._processLayoutQueue( queue );
29959 /** Sets position of item in DOM
29960 * @param {Element} item
29961 * @param {Number} x - horizontal position
29962 * @param {Number} y - vertical position
29963 * @param {Boolean} isInstant - disables transitions
29965 _processLayoutQueue : function( queue )
29967 for ( var i=0, len = queue.length; i < len; i++ ) {
29968 var obj = queue[i];
29969 obj.item.position('absolute');
29970 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29976 * Any logic you want to do after each layout,
29977 * i.e. size the container
29979 _postLayout : function()
29981 this.resizeContainer();
29984 resizeContainer : function()
29986 if ( !this.isResizingContainer ) {
29989 var size = this._getContainerSize();
29991 this.el.setSize(size.width,size.height);
29992 this.boxesEl.setSize(size.width,size.height);
29998 _resetLayout : function()
30000 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30001 this.colWidth = this.el.getWidth();
30002 //this.gutter = this.el.getWidth();
30004 this.measureColumns();
30010 this.colYs.push( 0 );
30016 measureColumns : function()
30018 this.getContainerWidth();
30019 // if columnWidth is 0, default to outerWidth of first item
30020 if ( !this.columnWidth ) {
30021 var firstItem = this.bricks.first();
30022 Roo.log(firstItem);
30023 this.columnWidth = this.containerWidth;
30024 if (firstItem && firstItem.attr('originalwidth') ) {
30025 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30027 // columnWidth fall back to item of first element
30028 Roo.log("set column width?");
30029 this.initialColumnWidth = this.columnWidth ;
30031 // if first elem has no width, default to size of container
30036 if (this.initialColumnWidth) {
30037 this.columnWidth = this.initialColumnWidth;
30042 // column width is fixed at the top - however if container width get's smaller we should
30045 // this bit calcs how man columns..
30047 var columnWidth = this.columnWidth += this.gutter;
30049 // calculate columns
30050 var containerWidth = this.containerWidth + this.gutter;
30052 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30053 // fix rounding errors, typically with gutters
30054 var excess = columnWidth - containerWidth % columnWidth;
30057 // if overshoot is less than a pixel, round up, otherwise floor it
30058 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30059 cols = Math[ mathMethod ]( cols );
30060 this.cols = Math.max( cols, 1 );
30061 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30063 // padding positioning..
30064 var totalColWidth = this.cols * this.columnWidth;
30065 var padavail = this.containerWidth - totalColWidth;
30066 // so for 2 columns - we need 3 'pads'
30068 var padNeeded = (1+this.cols) * this.padWidth;
30070 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30072 this.columnWidth += padExtra
30073 //this.padWidth = Math.floor(padavail / ( this.cols));
30075 // adjust colum width so that padding is fixed??
30077 // we have 3 columns ... total = width * 3
30078 // we have X left over... that should be used by
30080 //if (this.expandC) {
30088 getContainerWidth : function()
30090 /* // container is parent if fit width
30091 var container = this.isFitWidth ? this.element.parentNode : this.element;
30092 // check that this.size and size are there
30093 // IE8 triggers resize on body size change, so they might not be
30095 var size = getSize( container ); //FIXME
30096 this.containerWidth = size && size.innerWidth; //FIXME
30099 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30103 _getItemLayoutPosition : function( item ) // what is item?
30105 // we resize the item to our columnWidth..
30107 item.setWidth(this.columnWidth);
30108 item.autoBoxAdjust = false;
30110 var sz = item.getSize();
30112 // how many columns does this brick span
30113 var remainder = this.containerWidth % this.columnWidth;
30115 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30116 // round if off by 1 pixel, otherwise use ceil
30117 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30118 colSpan = Math.min( colSpan, this.cols );
30120 // normally this should be '1' as we dont' currently allow multi width columns..
30122 var colGroup = this._getColGroup( colSpan );
30123 // get the minimum Y value from the columns
30124 var minimumY = Math.min.apply( Math, colGroup );
30125 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30127 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30129 // position the brick
30131 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30132 y: this.currentSize.y + minimumY + this.padHeight
30136 // apply setHeight to necessary columns
30137 var setHeight = minimumY + sz.height + this.padHeight;
30138 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30140 var setSpan = this.cols + 1 - colGroup.length;
30141 for ( var i = 0; i < setSpan; i++ ) {
30142 this.colYs[ shortColIndex + i ] = setHeight ;
30149 * @param {Number} colSpan - number of columns the element spans
30150 * @returns {Array} colGroup
30152 _getColGroup : function( colSpan )
30154 if ( colSpan < 2 ) {
30155 // if brick spans only one column, use all the column Ys
30160 // how many different places could this brick fit horizontally
30161 var groupCount = this.cols + 1 - colSpan;
30162 // for each group potential horizontal position
30163 for ( var i = 0; i < groupCount; i++ ) {
30164 // make an array of colY values for that one group
30165 var groupColYs = this.colYs.slice( i, i + colSpan );
30166 // and get the max value of the array
30167 colGroup[i] = Math.max.apply( Math, groupColYs );
30172 _manageStamp : function( stamp )
30174 var stampSize = stamp.getSize();
30175 var offset = stamp.getBox();
30176 // get the columns that this stamp affects
30177 var firstX = this.isOriginLeft ? offset.x : offset.right;
30178 var lastX = firstX + stampSize.width;
30179 var firstCol = Math.floor( firstX / this.columnWidth );
30180 firstCol = Math.max( 0, firstCol );
30182 var lastCol = Math.floor( lastX / this.columnWidth );
30183 // lastCol should not go over if multiple of columnWidth #425
30184 lastCol -= lastX % this.columnWidth ? 0 : 1;
30185 lastCol = Math.min( this.cols - 1, lastCol );
30187 // set colYs to bottom of the stamp
30188 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30191 for ( var i = firstCol; i <= lastCol; i++ ) {
30192 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30197 _getContainerSize : function()
30199 this.maxY = Math.max.apply( Math, this.colYs );
30204 if ( this.isFitWidth ) {
30205 size.width = this._getContainerFitWidth();
30211 _getContainerFitWidth : function()
30213 var unusedCols = 0;
30214 // count unused columns
30217 if ( this.colYs[i] !== 0 ) {
30222 // fit container to columns that have been used
30223 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30226 needsResizeLayout : function()
30228 var previousWidth = this.containerWidth;
30229 this.getContainerWidth();
30230 return previousWidth !== this.containerWidth;
30245 * @class Roo.bootstrap.MasonryBrick
30246 * @extends Roo.bootstrap.Component
30247 * Bootstrap MasonryBrick class
30250 * Create a new MasonryBrick
30251 * @param {Object} config The config object
30254 Roo.bootstrap.MasonryBrick = function(config){
30255 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30261 * When a MasonryBrick is clcik
30262 * @param {Roo.bootstrap.MasonryBrick} this
30263 * @param {Roo.EventObject} e
30269 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
30272 * @cfg {String} title
30276 * @cfg {String} html
30280 * @cfg {String} bgimage
30284 * @cfg {String} videourl
30288 * @cfg {String} cls
30292 * @cfg {String} href
30296 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30301 * @cfg {String} (center|bottom) placetitle
30305 getAutoCreate : function()
30307 var cls = 'masonry-brick';
30309 if(this.href.length){
30310 cls += ' masonry-brick-link';
30313 if(this.bgimage.length){
30314 cls += ' masonry-brick-image';
30318 cls += ' masonry-' + this.size + '-brick';
30321 if(this.placetitle.length){
30323 switch (this.placetitle) {
30325 cls += ' masonry-center-title';
30328 cls += ' masonry-bottom-title';
30335 if(!this.html.length && !this.bgimage.length){
30336 cls += ' masonry-center-title';
30339 if(!this.html.length && this.bgimage.length){
30340 cls += ' masonry-bottom-title';
30345 cls += ' ' + this.cls;
30349 tag: (this.href.length) ? 'a' : 'div',
30354 cls: 'masonry-brick-paragraph',
30360 if(this.href.length){
30361 cfg.href = this.href;
30364 var cn = cfg.cn[0].cn;
30366 if(this.title.length){
30369 cls: 'masonry-brick-title',
30374 if(this.html.length){
30377 cls: 'masonry-brick-text',
30381 if (!this.title.length && !this.html.length) {
30382 cfg.cn[0].cls += ' hide';
30385 if(this.bgimage.length){
30388 cls: 'masonry-brick-image-view',
30392 if(this.videourl.length){
30393 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30394 // youtube support only?
30397 cls: 'masonry-brick-image-view',
30400 allowfullscreen : true
30409 initEvents: function()
30411 switch (this.size) {
30413 // this.intSize = 1;
30418 // this.intSize = 2;
30425 // this.intSize = 3;
30430 // this.intSize = 3;
30435 // this.intSize = 3;
30440 // this.intSize = 3;
30452 this.el.on('touchstart', this.onTouchStart, this);
30453 this.el.on('touchmove', this.onTouchMove, this);
30454 this.el.on('touchend', this.onTouchEnd, this);
30455 this.el.on('contextmenu', this.onContextMenu, this);
30457 this.el.on('mouseenter' ,this.enter, this);
30458 this.el.on('mouseleave', this.leave, this);
30461 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30462 this.parent().bricks.push(this);
30467 onClick: function(e, el)
30473 var time = this.endTimer - this.startTimer;
30481 e.preventDefault();
30484 enter: function(e, el)
30486 e.preventDefault();
30488 if(this.bgimage.length && this.html.length){
30489 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30493 leave: function(e, el)
30495 e.preventDefault();
30497 if(this.bgimage.length && this.html.length){
30498 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30502 onTouchStart: function(e, el)
30504 // e.preventDefault();
30506 this.touchmoved = false;
30508 if(!this.bgimage.length || !this.html.length){
30512 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30514 this.timer = new Date().getTime();
30518 onTouchMove: function(e, el)
30520 this.touchmoved = true;
30523 onContextMenu : function(e,el)
30525 e.preventDefault();
30526 e.stopPropagation();
30530 onTouchEnd: function(e, el)
30532 // e.preventDefault();
30534 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30541 if(!this.bgimage.length || !this.html.length){
30543 if(this.href.length){
30544 window.location.href = this.href;
30550 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30552 window.location.href = this.href;
30567 * @class Roo.bootstrap.Brick
30568 * @extends Roo.bootstrap.Component
30569 * Bootstrap Brick class
30572 * Create a new Brick
30573 * @param {Object} config The config object
30576 Roo.bootstrap.Brick = function(config){
30577 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30583 * When a Brick is click
30584 * @param {Roo.bootstrap.Brick} this
30585 * @param {Roo.EventObject} e
30591 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
30594 * @cfg {String} title
30598 * @cfg {String} html
30602 * @cfg {String} bgimage
30606 * @cfg {String} cls
30610 * @cfg {String} href
30614 * @cfg {String} video
30618 * @cfg {Boolean} square
30622 getAutoCreate : function()
30624 var cls = 'roo-brick';
30626 if(this.href.length){
30627 cls += ' roo-brick-link';
30630 if(this.bgimage.length){
30631 cls += ' roo-brick-image';
30634 if(!this.html.length && !this.bgimage.length){
30635 cls += ' roo-brick-center-title';
30638 if(!this.html.length && this.bgimage.length){
30639 cls += ' roo-brick-bottom-title';
30643 cls += ' ' + this.cls;
30647 tag: (this.href.length) ? 'a' : 'div',
30652 cls: 'roo-brick-paragraph',
30658 if(this.href.length){
30659 cfg.href = this.href;
30662 var cn = cfg.cn[0].cn;
30664 if(this.title.length){
30667 cls: 'roo-brick-title',
30672 if(this.html.length){
30675 cls: 'roo-brick-text',
30682 if(this.bgimage.length){
30685 cls: 'roo-brick-image-view',
30693 initEvents: function()
30695 if(this.title.length || this.html.length){
30696 this.el.on('mouseenter' ,this.enter, this);
30697 this.el.on('mouseleave', this.leave, this);
30701 Roo.EventManager.onWindowResize(this.resize, this);
30706 resize : function()
30708 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30710 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30711 // paragraph.setHeight(paragraph.getWidth());
30713 if(this.bgimage.length){
30714 var image = this.el.select('.roo-brick-image-view', true).first();
30715 image.setWidth(paragraph.getWidth());
30716 image.setHeight(paragraph.getWidth());
30721 enter: function(e, el)
30723 e.preventDefault();
30725 if(this.bgimage.length){
30726 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30727 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30731 leave: function(e, el)
30733 e.preventDefault();
30735 if(this.bgimage.length){
30736 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30737 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30747 * Ext JS Library 1.1.1
30748 * Copyright(c) 2006-2007, Ext JS, LLC.
30750 * Originally Released Under LGPL - original licence link has changed is not relivant.
30753 * <script type="text/javascript">
30758 * @class Roo.bootstrap.SplitBar
30759 * @extends Roo.util.Observable
30760 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30764 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30765 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30766 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30767 split.minSize = 100;
30768 split.maxSize = 600;
30769 split.animate = true;
30770 split.on('moved', splitterMoved);
30773 * Create a new SplitBar
30774 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
30775 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
30776 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30777 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
30778 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30779 position of the SplitBar).
30781 Roo.bootstrap.SplitBar = function(cfg){
30786 // dragElement : elm
30787 // resizingElement: el,
30789 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30790 // placement : Roo.bootstrap.SplitBar.LEFT ,
30791 // existingProxy ???
30794 this.el = Roo.get(cfg.dragElement, true);
30795 this.el.dom.unselectable = "on";
30797 this.resizingEl = Roo.get(cfg.resizingElement, true);
30801 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30802 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30805 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30808 * The minimum size of the resizing element. (Defaults to 0)
30814 * The maximum size of the resizing element. (Defaults to 2000)
30817 this.maxSize = 2000;
30820 * Whether to animate the transition to the new size
30823 this.animate = false;
30826 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30829 this.useShim = false;
30834 if(!cfg.existingProxy){
30836 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30838 this.proxy = Roo.get(cfg.existingProxy).dom;
30841 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30844 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30847 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30850 this.dragSpecs = {};
30853 * @private The adapter to use to positon and resize elements
30855 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30856 this.adapter.init(this);
30858 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30860 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30861 this.el.addClass("roo-splitbar-h");
30864 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30865 this.el.addClass("roo-splitbar-v");
30871 * Fires when the splitter is moved (alias for {@link #event-moved})
30872 * @param {Roo.bootstrap.SplitBar} this
30873 * @param {Number} newSize the new width or height
30878 * Fires when the splitter is moved
30879 * @param {Roo.bootstrap.SplitBar} this
30880 * @param {Number} newSize the new width or height
30884 * @event beforeresize
30885 * Fires before the splitter is dragged
30886 * @param {Roo.bootstrap.SplitBar} this
30888 "beforeresize" : true,
30890 "beforeapply" : true
30893 Roo.util.Observable.call(this);
30896 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30897 onStartProxyDrag : function(x, y){
30898 this.fireEvent("beforeresize", this);
30900 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
30902 o.enableDisplayMode("block");
30903 // all splitbars share the same overlay
30904 Roo.bootstrap.SplitBar.prototype.overlay = o;
30906 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30907 this.overlay.show();
30908 Roo.get(this.proxy).setDisplayed("block");
30909 var size = this.adapter.getElementSize(this);
30910 this.activeMinSize = this.getMinimumSize();;
30911 this.activeMaxSize = this.getMaximumSize();;
30912 var c1 = size - this.activeMinSize;
30913 var c2 = Math.max(this.activeMaxSize - size, 0);
30914 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30915 this.dd.resetConstraints();
30916 this.dd.setXConstraint(
30917 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
30918 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30920 this.dd.setYConstraint(0, 0);
30922 this.dd.resetConstraints();
30923 this.dd.setXConstraint(0, 0);
30924 this.dd.setYConstraint(
30925 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
30926 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30929 this.dragSpecs.startSize = size;
30930 this.dragSpecs.startPoint = [x, y];
30931 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30935 * @private Called after the drag operation by the DDProxy
30937 onEndProxyDrag : function(e){
30938 Roo.get(this.proxy).setDisplayed(false);
30939 var endPoint = Roo.lib.Event.getXY(e);
30941 this.overlay.hide();
30944 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30945 newSize = this.dragSpecs.startSize +
30946 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30947 endPoint[0] - this.dragSpecs.startPoint[0] :
30948 this.dragSpecs.startPoint[0] - endPoint[0]
30951 newSize = this.dragSpecs.startSize +
30952 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30953 endPoint[1] - this.dragSpecs.startPoint[1] :
30954 this.dragSpecs.startPoint[1] - endPoint[1]
30957 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30958 if(newSize != this.dragSpecs.startSize){
30959 if(this.fireEvent('beforeapply', this, newSize) !== false){
30960 this.adapter.setElementSize(this, newSize);
30961 this.fireEvent("moved", this, newSize);
30962 this.fireEvent("resize", this, newSize);
30968 * Get the adapter this SplitBar uses
30969 * @return The adapter object
30971 getAdapter : function(){
30972 return this.adapter;
30976 * Set the adapter this SplitBar uses
30977 * @param {Object} adapter A SplitBar adapter object
30979 setAdapter : function(adapter){
30980 this.adapter = adapter;
30981 this.adapter.init(this);
30985 * Gets the minimum size for the resizing element
30986 * @return {Number} The minimum size
30988 getMinimumSize : function(){
30989 return this.minSize;
30993 * Sets the minimum size for the resizing element
30994 * @param {Number} minSize The minimum size
30996 setMinimumSize : function(minSize){
30997 this.minSize = minSize;
31001 * Gets the maximum size for the resizing element
31002 * @return {Number} The maximum size
31004 getMaximumSize : function(){
31005 return this.maxSize;
31009 * Sets the maximum size for the resizing element
31010 * @param {Number} maxSize The maximum size
31012 setMaximumSize : function(maxSize){
31013 this.maxSize = maxSize;
31017 * Sets the initialize size for the resizing element
31018 * @param {Number} size The initial size
31020 setCurrentSize : function(size){
31021 var oldAnimate = this.animate;
31022 this.animate = false;
31023 this.adapter.setElementSize(this, size);
31024 this.animate = oldAnimate;
31028 * Destroy this splitbar.
31029 * @param {Boolean} removeEl True to remove the element
31031 destroy : function(removeEl){
31033 this.shim.remove();
31036 this.proxy.parentNode.removeChild(this.proxy);
31044 * @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.
31046 Roo.bootstrap.SplitBar.createProxy = function(dir){
31047 var proxy = new Roo.Element(document.createElement("div"));
31048 proxy.unselectable();
31049 var cls = 'roo-splitbar-proxy';
31050 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31051 document.body.appendChild(proxy.dom);
31056 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31057 * Default Adapter. It assumes the splitter and resizing element are not positioned
31058 * elements and only gets/sets the width of the element. Generally used for table based layouts.
31060 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31063 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31064 // do nothing for now
31065 init : function(s){
31069 * Called before drag operations to get the current size of the resizing element.
31070 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31072 getElementSize : function(s){
31073 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31074 return s.resizingEl.getWidth();
31076 return s.resizingEl.getHeight();
31081 * Called after drag operations to set the size of the resizing element.
31082 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31083 * @param {Number} newSize The new size to set
31084 * @param {Function} onComplete A function to be invoked when resizing is complete
31086 setElementSize : function(s, newSize, onComplete){
31087 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31089 s.resizingEl.setWidth(newSize);
31091 onComplete(s, newSize);
31094 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31099 s.resizingEl.setHeight(newSize);
31101 onComplete(s, newSize);
31104 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31111 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31112 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31113 * Adapter that moves the splitter element to align with the resized sizing element.
31114 * Used with an absolute positioned SplitBar.
31115 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31116 * document.body, make sure you assign an id to the body element.
31118 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31119 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31120 this.container = Roo.get(container);
31123 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31124 init : function(s){
31125 this.basic.init(s);
31128 getElementSize : function(s){
31129 return this.basic.getElementSize(s);
31132 setElementSize : function(s, newSize, onComplete){
31133 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31136 moveSplitter : function(s){
31137 var yes = Roo.bootstrap.SplitBar;
31138 switch(s.placement){
31140 s.el.setX(s.resizingEl.getRight());
31143 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31146 s.el.setY(s.resizingEl.getBottom());
31149 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31156 * Orientation constant - Create a vertical SplitBar
31160 Roo.bootstrap.SplitBar.VERTICAL = 1;
31163 * Orientation constant - Create a horizontal SplitBar
31167 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31170 * Placement constant - The resizing element is to the left of the splitter element
31174 Roo.bootstrap.SplitBar.LEFT = 1;
31177 * Placement constant - The resizing element is to the right of the splitter element
31181 Roo.bootstrap.SplitBar.RIGHT = 2;
31184 * Placement constant - The resizing element is positioned above the splitter element
31188 Roo.bootstrap.SplitBar.TOP = 3;
31191 * Placement constant - The resizing element is positioned under splitter element
31195 Roo.bootstrap.SplitBar.BOTTOM = 4;
31196 Roo.namespace("Roo.bootstrap.layout");/*
31198 * Ext JS Library 1.1.1
31199 * Copyright(c) 2006-2007, Ext JS, LLC.
31201 * Originally Released Under LGPL - original licence link has changed is not relivant.
31204 * <script type="text/javascript">
31208 * @class Roo.bootstrap.layout.Manager
31209 * @extends Roo.bootstrap.Component
31210 * Base class for layout managers.
31212 Roo.bootstrap.layout.Manager = function(config)
31214 Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31220 /** false to disable window resize monitoring @type Boolean */
31221 this.monitorWindowResize = true;
31226 * Fires when a layout is performed.
31227 * @param {Roo.LayoutManager} this
31231 * @event regionresized
31232 * Fires when the user resizes a region.
31233 * @param {Roo.LayoutRegion} region The resized region
31234 * @param {Number} newSize The new size (width for east/west, height for north/south)
31236 "regionresized" : true,
31238 * @event regioncollapsed
31239 * Fires when a region is collapsed.
31240 * @param {Roo.LayoutRegion} region The collapsed region
31242 "regioncollapsed" : true,
31244 * @event regionexpanded
31245 * Fires when a region is expanded.
31246 * @param {Roo.LayoutRegion} region The expanded region
31248 "regionexpanded" : true
31250 this.updating = false;
31253 this.el = Roo.get(config.el);
31259 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31264 monitorWindowResize : true,
31270 onRender : function(ct, position)
31273 this.el = Roo.get(ct);
31279 initEvents: function()
31283 // ie scrollbar fix
31284 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31285 document.body.scroll = "no";
31286 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31287 this.el.position('relative');
31289 this.id = this.el.id;
31290 this.el.addClass("roo-layout-container");
31291 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31292 if(this.el.dom != document.body ) {
31293 this.el.on('resize', this.layout,this);
31294 this.el.on('show', this.layout,this);
31300 * Returns true if this layout is currently being updated
31301 * @return {Boolean}
31303 isUpdating : function(){
31304 return this.updating;
31308 * Suspend the LayoutManager from doing auto-layouts while
31309 * making multiple add or remove calls
31311 beginUpdate : function(){
31312 this.updating = true;
31316 * Restore auto-layouts and optionally disable the manager from performing a layout
31317 * @param {Boolean} noLayout true to disable a layout update
31319 endUpdate : function(noLayout){
31320 this.updating = false;
31326 layout: function(){
31330 onRegionResized : function(region, newSize){
31331 this.fireEvent("regionresized", region, newSize);
31335 onRegionCollapsed : function(region){
31336 this.fireEvent("regioncollapsed", region);
31339 onRegionExpanded : function(region){
31340 this.fireEvent("regionexpanded", region);
31344 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31345 * performs box-model adjustments.
31346 * @return {Object} The size as an object {width: (the width), height: (the height)}
31348 getViewSize : function()
31351 if(this.el.dom != document.body){
31352 size = this.el.getSize();
31354 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31356 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31357 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31362 * Returns the Element this layout is bound to.
31363 * @return {Roo.Element}
31365 getEl : function(){
31370 * Returns the specified region.
31371 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31372 * @return {Roo.LayoutRegion}
31374 getRegion : function(target){
31375 return this.regions[target.toLowerCase()];
31378 onWindowResize : function(){
31379 if(this.monitorWindowResize){
31385 * Ext JS Library 1.1.1
31386 * Copyright(c) 2006-2007, Ext JS, LLC.
31388 * Originally Released Under LGPL - original licence link has changed is not relivant.
31391 * <script type="text/javascript">
31394 * @class Roo.bootstrap.layout.Border
31395 * @extends Roo.bootstrap.layout.Manager
31396 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31397 * please see: examples/bootstrap/nested.html<br><br>
31399 <b>The container the layout is rendered into can be either the body element or any other element.
31400 If it is not the body element, the container needs to either be an absolute positioned element,
31401 or you will need to add "position:relative" to the css of the container. You will also need to specify
31402 the container size if it is not the body element.</b>
31405 * Create a new Border
31406 * @param {Object} config Configuration options
31408 Roo.bootstrap.layout.Border = function(config){
31409 config = config || {};
31410 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31414 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31415 if(config[region]){
31416 config[region].region = region;
31417 this.addRegion(config[region]);
31423 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
31425 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31427 * Creates and adds a new region if it doesn't already exist.
31428 * @param {String} target The target region key (north, south, east, west or center).
31429 * @param {Object} config The regions config object
31430 * @return {BorderLayoutRegion} The new region
31432 addRegion : function(config)
31434 if(!this.regions[config.region]){
31435 var r = this.factory(config);
31436 this.bindRegion(r);
31438 return this.regions[config.region];
31442 bindRegion : function(r){
31443 this.regions[r.config.region] = r;
31445 r.on("visibilitychange", this.layout, this);
31446 r.on("paneladded", this.layout, this);
31447 r.on("panelremoved", this.layout, this);
31448 r.on("invalidated", this.layout, this);
31449 r.on("resized", this.onRegionResized, this);
31450 r.on("collapsed", this.onRegionCollapsed, this);
31451 r.on("expanded", this.onRegionExpanded, this);
31455 * Performs a layout update.
31457 layout : function()
31459 if(this.updating) {
31462 var size = this.getViewSize();
31463 var w = size.width;
31464 var h = size.height;
31469 //var x = 0, y = 0;
31471 var rs = this.regions;
31472 var north = rs["north"];
31473 var south = rs["south"];
31474 var west = rs["west"];
31475 var east = rs["east"];
31476 var center = rs["center"];
31477 //if(this.hideOnLayout){ // not supported anymore
31478 //c.el.setStyle("display", "none");
31480 if(north && north.isVisible()){
31481 var b = north.getBox();
31482 var m = north.getMargins();
31483 b.width = w - (m.left+m.right);
31486 centerY = b.height + b.y + m.bottom;
31487 centerH -= centerY;
31488 north.updateBox(this.safeBox(b));
31490 if(south && south.isVisible()){
31491 var b = south.getBox();
31492 var m = south.getMargins();
31493 b.width = w - (m.left+m.right);
31495 var totalHeight = (b.height + m.top + m.bottom);
31496 b.y = h - totalHeight + m.top;
31497 centerH -= totalHeight;
31498 south.updateBox(this.safeBox(b));
31500 if(west && west.isVisible()){
31501 var b = west.getBox();
31502 var m = west.getMargins();
31503 b.height = centerH - (m.top+m.bottom);
31505 b.y = centerY + m.top;
31506 var totalWidth = (b.width + m.left + m.right);
31507 centerX += totalWidth;
31508 centerW -= totalWidth;
31509 west.updateBox(this.safeBox(b));
31511 if(east && east.isVisible()){
31512 var b = east.getBox();
31513 var m = east.getMargins();
31514 b.height = centerH - (m.top+m.bottom);
31515 var totalWidth = (b.width + m.left + m.right);
31516 b.x = w - totalWidth + m.left;
31517 b.y = centerY + m.top;
31518 centerW -= totalWidth;
31519 east.updateBox(this.safeBox(b));
31522 var m = center.getMargins();
31524 x: centerX + m.left,
31525 y: centerY + m.top,
31526 width: centerW - (m.left+m.right),
31527 height: centerH - (m.top+m.bottom)
31529 //if(this.hideOnLayout){
31530 //center.el.setStyle("display", "block");
31532 center.updateBox(this.safeBox(centerBox));
31535 this.fireEvent("layout", this);
31539 safeBox : function(box){
31540 box.width = Math.max(0, box.width);
31541 box.height = Math.max(0, box.height);
31546 * Adds a ContentPanel (or subclass) to this layout.
31547 * @param {String} target The target region key (north, south, east, west or center).
31548 * @param {Roo.ContentPanel} panel The panel to add
31549 * @return {Roo.ContentPanel} The added panel
31551 add : function(target, panel){
31553 target = target.toLowerCase();
31554 return this.regions[target].add(panel);
31558 * Remove a ContentPanel (or subclass) to this layout.
31559 * @param {String} target The target region key (north, south, east, west or center).
31560 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31561 * @return {Roo.ContentPanel} The removed panel
31563 remove : function(target, panel){
31564 target = target.toLowerCase();
31565 return this.regions[target].remove(panel);
31569 * Searches all regions for a panel with the specified id
31570 * @param {String} panelId
31571 * @return {Roo.ContentPanel} The panel or null if it wasn't found
31573 findPanel : function(panelId){
31574 var rs = this.regions;
31575 for(var target in rs){
31576 if(typeof rs[target] != "function"){
31577 var p = rs[target].getPanel(panelId);
31587 * Searches all regions for a panel with the specified id and activates (shows) it.
31588 * @param {String/ContentPanel} panelId The panels id or the panel itself
31589 * @return {Roo.ContentPanel} The shown panel or null
31591 showPanel : function(panelId) {
31592 var rs = this.regions;
31593 for(var target in rs){
31594 var r = rs[target];
31595 if(typeof r != "function"){
31596 if(r.hasPanel(panelId)){
31597 return r.showPanel(panelId);
31605 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31606 * @param {Roo.state.Provider} provider (optional) An alternate state provider
31609 restoreState : function(provider){
31611 provider = Roo.state.Manager;
31613 var sm = new Roo.LayoutStateManager();
31614 sm.init(this, provider);
31620 * Adds a xtype elements to the layout.
31624 xtype : 'ContentPanel',
31631 xtype : 'NestedLayoutPanel',
31637 items : [ ... list of content panels or nested layout panels.. ]
31641 * @param {Object} cfg Xtype definition of item to add.
31643 addxtype : function(cfg)
31645 // basically accepts a pannel...
31646 // can accept a layout region..!?!?
31647 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31650 // theory? children can only be panels??
31652 //if (!cfg.xtype.match(/Panel$/)) {
31657 if (typeof(cfg.region) == 'undefined') {
31658 Roo.log("Failed to add Panel, region was not set");
31662 var region = cfg.region;
31668 xitems = cfg.items;
31675 case 'Content': // ContentPanel (el, cfg)
31676 case 'Scroll': // ContentPanel (el, cfg)
31678 cfg.autoCreate = true;
31679 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31681 // var el = this.el.createChild();
31682 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31685 this.add(region, ret);
31689 case 'TreePanel': // our new panel!
31690 cfg.el = this.el.createChild();
31691 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31692 this.add(region, ret);
31697 // create a new Layout (which is a Border Layout...
31699 var clayout = cfg.layout;
31700 clayout.el = this.el.createChild();
31701 clayout.items = clayout.items || [];
31705 // replace this exitems with the clayout ones..
31706 xitems = clayout.items;
31708 // force background off if it's in center...
31709 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31710 cfg.background = false;
31712 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
31715 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31716 //console.log('adding nested layout panel ' + cfg.toSource());
31717 this.add(region, ret);
31718 nb = {}; /// find first...
31723 // needs grid and region
31725 //var el = this.getRegion(region).el.createChild();
31727 *var el = this.el.createChild();
31728 // create the grid first...
31729 cfg.grid.container = el;
31730 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31733 if (region == 'center' && this.active ) {
31734 cfg.background = false;
31737 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31739 this.add(region, ret);
31741 if (cfg.background) {
31742 // render grid on panel activation (if panel background)
31743 ret.on('activate', function(gp) {
31744 if (!gp.grid.rendered) {
31745 // gp.grid.render(el);
31749 // cfg.grid.render(el);
31755 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31756 // it was the old xcomponent building that caused this before.
31757 // espeically if border is the top element in the tree.
31767 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31769 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31770 this.add(region, ret);
31774 throw "Can not add '" + cfg.xtype + "' to Border";
31780 this.beginUpdate();
31784 Roo.each(xitems, function(i) {
31785 region = nb && i.region ? i.region : false;
31787 var add = ret.addxtype(i);
31790 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31791 if (!i.background) {
31792 abn[region] = nb[region] ;
31799 // make the last non-background panel active..
31800 //if (nb) { Roo.log(abn); }
31803 for(var r in abn) {
31804 region = this.getRegion(r);
31806 // tried using nb[r], but it does not work..
31808 region.showPanel(abn[r]);
31819 factory : function(cfg)
31822 var validRegions = Roo.bootstrap.layout.Border.regions;
31824 var target = cfg.region;
31827 var r = Roo.bootstrap.layout;
31831 return new r.North(cfg);
31833 return new r.South(cfg);
31835 return new r.East(cfg);
31837 return new r.West(cfg);
31839 return new r.Center(cfg);
31841 throw 'Layout region "'+target+'" not supported.';
31848 * Ext JS Library 1.1.1
31849 * Copyright(c) 2006-2007, Ext JS, LLC.
31851 * Originally Released Under LGPL - original licence link has changed is not relivant.
31854 * <script type="text/javascript">
31858 * @class Roo.bootstrap.layout.Basic
31859 * @extends Roo.util.Observable
31860 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31861 * and does not have a titlebar, tabs or any other features. All it does is size and position
31862 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31863 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
31864 * @cfg {string} region the region that it inhabits..
31865 * @cfg {bool} skipConfig skip config?
31869 Roo.bootstrap.layout.Basic = function(config){
31871 this.mgr = config.mgr;
31873 this.position = config.region;
31875 var skipConfig = config.skipConfig;
31879 * @scope Roo.BasicLayoutRegion
31883 * @event beforeremove
31884 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31885 * @param {Roo.LayoutRegion} this
31886 * @param {Roo.ContentPanel} panel The panel
31887 * @param {Object} e The cancel event object
31889 "beforeremove" : true,
31891 * @event invalidated
31892 * Fires when the layout for this region is changed.
31893 * @param {Roo.LayoutRegion} this
31895 "invalidated" : true,
31897 * @event visibilitychange
31898 * Fires when this region is shown or hidden
31899 * @param {Roo.LayoutRegion} this
31900 * @param {Boolean} visibility true or false
31902 "visibilitychange" : true,
31904 * @event paneladded
31905 * Fires when a panel is added.
31906 * @param {Roo.LayoutRegion} this
31907 * @param {Roo.ContentPanel} panel The panel
31909 "paneladded" : true,
31911 * @event panelremoved
31912 * Fires when a panel is removed.
31913 * @param {Roo.LayoutRegion} this
31914 * @param {Roo.ContentPanel} panel The panel
31916 "panelremoved" : true,
31918 * @event beforecollapse
31919 * Fires when this region before collapse.
31920 * @param {Roo.LayoutRegion} this
31922 "beforecollapse" : true,
31925 * Fires when this region is collapsed.
31926 * @param {Roo.LayoutRegion} this
31928 "collapsed" : true,
31931 * Fires when this region is expanded.
31932 * @param {Roo.LayoutRegion} this
31937 * Fires when this region is slid into view.
31938 * @param {Roo.LayoutRegion} this
31940 "slideshow" : true,
31943 * Fires when this region slides out of view.
31944 * @param {Roo.LayoutRegion} this
31946 "slidehide" : true,
31948 * @event panelactivated
31949 * Fires when a panel is activated.
31950 * @param {Roo.LayoutRegion} this
31951 * @param {Roo.ContentPanel} panel The activated panel
31953 "panelactivated" : true,
31956 * Fires when the user resizes this region.
31957 * @param {Roo.LayoutRegion} this
31958 * @param {Number} newSize The new size (width for east/west, height for north/south)
31962 /** A collection of panels in this region. @type Roo.util.MixedCollection */
31963 this.panels = new Roo.util.MixedCollection();
31964 this.panels.getKey = this.getPanelId.createDelegate(this);
31966 this.activePanel = null;
31967 // ensure listeners are added...
31969 if (config.listeners || config.events) {
31970 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31971 listeners : config.listeners || {},
31972 events : config.events || {}
31976 if(skipConfig !== true){
31977 this.applyConfig(config);
31981 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31983 getPanelId : function(p){
31987 applyConfig : function(config){
31988 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31989 this.config = config;
31994 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
31995 * the width, for horizontal (north, south) the height.
31996 * @param {Number} newSize The new width or height
31998 resizeTo : function(newSize){
31999 var el = this.el ? this.el :
32000 (this.activePanel ? this.activePanel.getEl() : null);
32002 switch(this.position){
32005 el.setWidth(newSize);
32006 this.fireEvent("resized", this, newSize);
32010 el.setHeight(newSize);
32011 this.fireEvent("resized", this, newSize);
32017 getBox : function(){
32018 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32021 getMargins : function(){
32022 return this.margins;
32025 updateBox : function(box){
32027 var el = this.activePanel.getEl();
32028 el.dom.style.left = box.x + "px";
32029 el.dom.style.top = box.y + "px";
32030 this.activePanel.setSize(box.width, box.height);
32034 * Returns the container element for this region.
32035 * @return {Roo.Element}
32037 getEl : function(){
32038 return this.activePanel;
32042 * Returns true if this region is currently visible.
32043 * @return {Boolean}
32045 isVisible : function(){
32046 return this.activePanel ? true : false;
32049 setActivePanel : function(panel){
32050 panel = this.getPanel(panel);
32051 if(this.activePanel && this.activePanel != panel){
32052 this.activePanel.setActiveState(false);
32053 this.activePanel.getEl().setLeftTop(-10000,-10000);
32055 this.activePanel = panel;
32056 panel.setActiveState(true);
32058 panel.setSize(this.box.width, this.box.height);
32060 this.fireEvent("panelactivated", this, panel);
32061 this.fireEvent("invalidated");
32065 * Show the specified panel.
32066 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32067 * @return {Roo.ContentPanel} The shown panel or null
32069 showPanel : function(panel){
32070 panel = this.getPanel(panel);
32072 this.setActivePanel(panel);
32078 * Get the active panel for this region.
32079 * @return {Roo.ContentPanel} The active panel or null
32081 getActivePanel : function(){
32082 return this.activePanel;
32086 * Add the passed ContentPanel(s)
32087 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32088 * @return {Roo.ContentPanel} The panel added (if only one was added)
32090 add : function(panel){
32091 if(arguments.length > 1){
32092 for(var i = 0, len = arguments.length; i < len; i++) {
32093 this.add(arguments[i]);
32097 if(this.hasPanel(panel)){
32098 this.showPanel(panel);
32101 var el = panel.getEl();
32102 if(el.dom.parentNode != this.mgr.el.dom){
32103 this.mgr.el.dom.appendChild(el.dom);
32105 if(panel.setRegion){
32106 panel.setRegion(this);
32108 this.panels.add(panel);
32109 el.setStyle("position", "absolute");
32110 if(!panel.background){
32111 this.setActivePanel(panel);
32112 if(this.config.initialSize && this.panels.getCount()==1){
32113 this.resizeTo(this.config.initialSize);
32116 this.fireEvent("paneladded", this, panel);
32121 * Returns true if the panel is in this region.
32122 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32123 * @return {Boolean}
32125 hasPanel : function(panel){
32126 if(typeof panel == "object"){ // must be panel obj
32127 panel = panel.getId();
32129 return this.getPanel(panel) ? true : false;
32133 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32134 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32135 * @param {Boolean} preservePanel Overrides the config preservePanel option
32136 * @return {Roo.ContentPanel} The panel that was removed
32138 remove : function(panel, preservePanel){
32139 panel = this.getPanel(panel);
32144 this.fireEvent("beforeremove", this, panel, e);
32145 if(e.cancel === true){
32148 var panelId = panel.getId();
32149 this.panels.removeKey(panelId);
32154 * Returns the panel specified or null if it's not in this region.
32155 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32156 * @return {Roo.ContentPanel}
32158 getPanel : function(id){
32159 if(typeof id == "object"){ // must be panel obj
32162 return this.panels.get(id);
32166 * Returns this regions position (north/south/east/west/center).
32169 getPosition: function(){
32170 return this.position;
32174 * Ext JS Library 1.1.1
32175 * Copyright(c) 2006-2007, Ext JS, LLC.
32177 * Originally Released Under LGPL - original licence link has changed is not relivant.
32180 * <script type="text/javascript">
32184 * @class Roo.bootstrap.layout.Region
32185 * @extends Roo.bootstrap.layout.Basic
32186 * This class represents a region in a layout manager.
32188 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32189 * @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})
32190 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
32191 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
32192 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
32193 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
32194 * @cfg {String} title The title for the region (overrides panel titles)
32195 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
32196 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32197 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
32198 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32199 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
32200 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32201 * the space available, similar to FireFox 1.5 tabs (defaults to false)
32202 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
32203 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
32204 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
32206 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
32207 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
32208 * @cfg {Boolean} disableTabTips True to disable tab tooltips
32209 * @cfg {Number} width For East/West panels
32210 * @cfg {Number} height For North/South panels
32211 * @cfg {Boolean} split To show the splitter
32212 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
32214 * @cfg {string} cls Extra CSS classes to add to region
32216 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
32217 * @cfg {string} region the region that it inhabits..
32220 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
32221 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
32223 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
32224 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
32225 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
32227 Roo.bootstrap.layout.Region = function(config)
32229 this.applyConfig(config);
32231 var mgr = config.mgr;
32232 var pos = config.region;
32233 config.skipConfig = true;
32234 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32237 this.onRender(mgr.el);
32240 this.visible = true;
32241 this.collapsed = false;
32244 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32246 position: '', // set by wrapper (eg. north/south etc..)
32248 createBody : function(){
32249 /** This region's body element
32250 * @type Roo.Element */
32251 this.bodyEl = this.el.createChild({
32253 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32257 onRender: function(ctr, pos)
32259 var dh = Roo.DomHelper;
32260 /** This region's container element
32261 * @type Roo.Element */
32262 this.el = dh.append(ctr.dom, {
32264 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32266 /** This region's title element
32267 * @type Roo.Element */
32269 this.titleEl = dh.append(this.el.dom,
32272 unselectable: "on",
32273 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32275 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
32276 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32279 this.titleEl.enableDisplayMode();
32280 /** This region's title text element
32281 * @type HTMLElement */
32282 this.titleTextEl = this.titleEl.dom.firstChild;
32283 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32285 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32286 this.closeBtn.enableDisplayMode();
32287 this.closeBtn.on("click", this.closeClicked, this);
32288 this.closeBtn.hide();
32290 this.createBody(this.config);
32291 if(this.config.hideWhenEmpty){
32293 this.on("paneladded", this.validateVisibility, this);
32294 this.on("panelremoved", this.validateVisibility, this);
32296 if(this.autoScroll){
32297 this.bodyEl.setStyle("overflow", "auto");
32299 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32301 //if(c.titlebar !== false){
32302 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32303 this.titleEl.hide();
32305 this.titleEl.show();
32306 if(this.config.title){
32307 this.titleTextEl.innerHTML = this.config.title;
32311 if(this.config.collapsed){
32312 this.collapse(true);
32314 if(this.config.hidden){
32319 applyConfig : function(c)
32322 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32323 var dh = Roo.DomHelper;
32324 if(c.titlebar !== false){
32325 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32326 this.collapseBtn.on("click", this.collapse, this);
32327 this.collapseBtn.enableDisplayMode();
32329 if(c.showPin === true || this.showPin){
32330 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32331 this.stickBtn.enableDisplayMode();
32332 this.stickBtn.on("click", this.expand, this);
32333 this.stickBtn.hide();
32338 /** This region's collapsed element
32339 * @type Roo.Element */
32342 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32343 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32346 if(c.floatable !== false){
32347 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32348 this.collapsedEl.on("click", this.collapseClick, this);
32351 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32352 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32353 id: "message", unselectable: "on", style:{"float":"left"}});
32354 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32356 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32357 this.expandBtn.on("click", this.expand, this);
32361 if(this.collapseBtn){
32362 this.collapseBtn.setVisible(c.collapsible == true);
32365 this.cmargins = c.cmargins || this.cmargins ||
32366 (this.position == "west" || this.position == "east" ?
32367 {top: 0, left: 2, right:2, bottom: 0} :
32368 {top: 2, left: 0, right:0, bottom: 2});
32370 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32373 this.bottomTabs = c.tabPosition != "top";
32375 this.autoScroll = c.autoScroll || false;
32380 this.duration = c.duration || .30;
32381 this.slideDuration = c.slideDuration || .45;
32386 * Returns true if this region is currently visible.
32387 * @return {Boolean}
32389 isVisible : function(){
32390 return this.visible;
32394 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32395 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
32397 //setCollapsedTitle : function(title){
32398 // title = title || " ";
32399 // if(this.collapsedTitleTextEl){
32400 // this.collapsedTitleTextEl.innerHTML = title;
32404 getBox : function(){
32406 // if(!this.collapsed){
32407 b = this.el.getBox(false, true);
32409 // b = this.collapsedEl.getBox(false, true);
32414 getMargins : function(){
32415 return this.margins;
32416 //return this.collapsed ? this.cmargins : this.margins;
32419 highlight : function(){
32420 this.el.addClass("x-layout-panel-dragover");
32423 unhighlight : function(){
32424 this.el.removeClass("x-layout-panel-dragover");
32427 updateBox : function(box)
32430 if(!this.collapsed){
32431 this.el.dom.style.left = box.x + "px";
32432 this.el.dom.style.top = box.y + "px";
32433 this.updateBody(box.width, box.height);
32435 this.collapsedEl.dom.style.left = box.x + "px";
32436 this.collapsedEl.dom.style.top = box.y + "px";
32437 this.collapsedEl.setSize(box.width, box.height);
32440 this.tabs.autoSizeTabs();
32444 updateBody : function(w, h)
32447 this.el.setWidth(w);
32448 w -= this.el.getBorderWidth("rl");
32449 if(this.config.adjustments){
32450 w += this.config.adjustments[0];
32454 this.el.setHeight(h);
32455 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32456 h -= this.el.getBorderWidth("tb");
32457 if(this.config.adjustments){
32458 h += this.config.adjustments[1];
32460 this.bodyEl.setHeight(h);
32462 h = this.tabs.syncHeight(h);
32465 if(this.panelSize){
32466 w = w !== null ? w : this.panelSize.width;
32467 h = h !== null ? h : this.panelSize.height;
32469 if(this.activePanel){
32470 var el = this.activePanel.getEl();
32471 w = w !== null ? w : el.getWidth();
32472 h = h !== null ? h : el.getHeight();
32473 this.panelSize = {width: w, height: h};
32474 this.activePanel.setSize(w, h);
32476 if(Roo.isIE && this.tabs){
32477 this.tabs.el.repaint();
32482 * Returns the container element for this region.
32483 * @return {Roo.Element}
32485 getEl : function(){
32490 * Hides this region.
32493 //if(!this.collapsed){
32494 this.el.dom.style.left = "-2000px";
32497 // this.collapsedEl.dom.style.left = "-2000px";
32498 // this.collapsedEl.hide();
32500 this.visible = false;
32501 this.fireEvent("visibilitychange", this, false);
32505 * Shows this region if it was previously hidden.
32508 //if(!this.collapsed){
32511 // this.collapsedEl.show();
32513 this.visible = true;
32514 this.fireEvent("visibilitychange", this, true);
32517 closeClicked : function(){
32518 if(this.activePanel){
32519 this.remove(this.activePanel);
32523 collapseClick : function(e){
32525 e.stopPropagation();
32528 e.stopPropagation();
32534 * Collapses this region.
32535 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32538 collapse : function(skipAnim, skipCheck = false){
32539 if(this.collapsed) {
32543 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32545 this.collapsed = true;
32547 this.split.el.hide();
32549 if(this.config.animate && skipAnim !== true){
32550 this.fireEvent("invalidated", this);
32551 this.animateCollapse();
32553 this.el.setLocation(-20000,-20000);
32555 this.collapsedEl.show();
32556 this.fireEvent("collapsed", this);
32557 this.fireEvent("invalidated", this);
32563 animateCollapse : function(){
32568 * Expands this region if it was previously collapsed.
32569 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32570 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32573 expand : function(e, skipAnim){
32575 e.stopPropagation();
32577 if(!this.collapsed || this.el.hasActiveFx()) {
32581 this.afterSlideIn();
32584 this.collapsed = false;
32585 if(this.config.animate && skipAnim !== true){
32586 this.animateExpand();
32590 this.split.el.show();
32592 this.collapsedEl.setLocation(-2000,-2000);
32593 this.collapsedEl.hide();
32594 this.fireEvent("invalidated", this);
32595 this.fireEvent("expanded", this);
32599 animateExpand : function(){
32603 initTabs : function()
32605 this.bodyEl.setStyle("overflow", "hidden");
32606 var ts = new Roo.bootstrap.panel.Tabs({
32607 el: this.bodyEl.dom,
32608 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32609 disableTooltips: this.config.disableTabTips,
32610 toolbar : this.config.toolbar
32613 if(this.config.hideTabs){
32614 ts.stripWrap.setDisplayed(false);
32617 ts.resizeTabs = this.config.resizeTabs === true;
32618 ts.minTabWidth = this.config.minTabWidth || 40;
32619 ts.maxTabWidth = this.config.maxTabWidth || 250;
32620 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32621 ts.monitorResize = false;
32622 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32623 ts.bodyEl.addClass('roo-layout-tabs-body');
32624 this.panels.each(this.initPanelAsTab, this);
32627 initPanelAsTab : function(panel){
32628 var ti = this.tabs.addTab(
32630 panel.getTitle(), null,
32631 this.config.closeOnTab && panel.isClosable()
32633 if(panel.tabTip !== undefined){
32634 ti.setTooltip(panel.tabTip);
32636 ti.on("activate", function(){
32637 this.setActivePanel(panel);
32640 if(this.config.closeOnTab){
32641 ti.on("beforeclose", function(t, e){
32643 this.remove(panel);
32649 updatePanelTitle : function(panel, title)
32651 if(this.activePanel == panel){
32652 this.updateTitle(title);
32655 var ti = this.tabs.getTab(panel.getEl().id);
32657 if(panel.tabTip !== undefined){
32658 ti.setTooltip(panel.tabTip);
32663 updateTitle : function(title){
32664 if(this.titleTextEl && !this.config.title){
32665 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
32669 setActivePanel : function(panel)
32671 panel = this.getPanel(panel);
32672 if(this.activePanel && this.activePanel != panel){
32673 this.activePanel.setActiveState(false);
32675 this.activePanel = panel;
32676 panel.setActiveState(true);
32677 if(this.panelSize){
32678 panel.setSize(this.panelSize.width, this.panelSize.height);
32681 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32683 this.updateTitle(panel.getTitle());
32685 this.fireEvent("invalidated", this);
32687 this.fireEvent("panelactivated", this, panel);
32691 * Shows the specified panel.
32692 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32693 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32695 showPanel : function(panel)
32697 panel = this.getPanel(panel);
32700 var tab = this.tabs.getTab(panel.getEl().id);
32701 if(tab.isHidden()){
32702 this.tabs.unhideTab(tab.id);
32706 this.setActivePanel(panel);
32713 * Get the active panel for this region.
32714 * @return {Roo.ContentPanel} The active panel or null
32716 getActivePanel : function(){
32717 return this.activePanel;
32720 validateVisibility : function(){
32721 if(this.panels.getCount() < 1){
32722 this.updateTitle(" ");
32723 this.closeBtn.hide();
32726 if(!this.isVisible()){
32733 * Adds the passed ContentPanel(s) to this region.
32734 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32735 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32737 add : function(panel){
32738 if(arguments.length > 1){
32739 for(var i = 0, len = arguments.length; i < len; i++) {
32740 this.add(arguments[i]);
32744 if(this.hasPanel(panel)){
32745 this.showPanel(panel);
32748 panel.setRegion(this);
32749 this.panels.add(panel);
32750 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32751 this.bodyEl.dom.appendChild(panel.getEl().dom);
32752 if(panel.background !== true){
32753 this.setActivePanel(panel);
32755 this.fireEvent("paneladded", this, panel);
32761 this.initPanelAsTab(panel);
32765 if(panel.background !== true){
32766 this.tabs.activate(panel.getEl().id);
32768 this.fireEvent("paneladded", this, panel);
32773 * Hides the tab for the specified panel.
32774 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32776 hidePanel : function(panel){
32777 if(this.tabs && (panel = this.getPanel(panel))){
32778 this.tabs.hideTab(panel.getEl().id);
32783 * Unhides the tab for a previously hidden panel.
32784 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32786 unhidePanel : function(panel){
32787 if(this.tabs && (panel = this.getPanel(panel))){
32788 this.tabs.unhideTab(panel.getEl().id);
32792 clearPanels : function(){
32793 while(this.panels.getCount() > 0){
32794 this.remove(this.panels.first());
32799 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32800 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32801 * @param {Boolean} preservePanel Overrides the config preservePanel option
32802 * @return {Roo.ContentPanel} The panel that was removed
32804 remove : function(panel, preservePanel)
32806 panel = this.getPanel(panel);
32811 this.fireEvent("beforeremove", this, panel, e);
32812 if(e.cancel === true){
32815 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32816 var panelId = panel.getId();
32817 this.panels.removeKey(panelId);
32819 document.body.appendChild(panel.getEl().dom);
32822 this.tabs.removeTab(panel.getEl().id);
32823 }else if (!preservePanel){
32824 this.bodyEl.dom.removeChild(panel.getEl().dom);
32826 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32827 var p = this.panels.first();
32828 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32829 tempEl.appendChild(p.getEl().dom);
32830 this.bodyEl.update("");
32831 this.bodyEl.dom.appendChild(p.getEl().dom);
32833 this.updateTitle(p.getTitle());
32835 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32836 this.setActivePanel(p);
32838 panel.setRegion(null);
32839 if(this.activePanel == panel){
32840 this.activePanel = null;
32842 if(this.config.autoDestroy !== false && preservePanel !== true){
32843 try{panel.destroy();}catch(e){}
32845 this.fireEvent("panelremoved", this, panel);
32850 * Returns the TabPanel component used by this region
32851 * @return {Roo.TabPanel}
32853 getTabs : function(){
32857 createTool : function(parentEl, className){
32858 var btn = Roo.DomHelper.append(parentEl, {
32860 cls: "x-layout-tools-button",
32863 cls: "roo-layout-tools-button-inner " + className,
32867 btn.addClassOnOver("roo-layout-tools-button-over");
32872 * Ext JS Library 1.1.1
32873 * Copyright(c) 2006-2007, Ext JS, LLC.
32875 * Originally Released Under LGPL - original licence link has changed is not relivant.
32878 * <script type="text/javascript">
32884 * @class Roo.SplitLayoutRegion
32885 * @extends Roo.LayoutRegion
32886 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32888 Roo.bootstrap.layout.Split = function(config){
32889 this.cursor = config.cursor;
32890 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32893 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32895 splitTip : "Drag to resize.",
32896 collapsibleSplitTip : "Drag to resize. Double click to hide.",
32897 useSplitTips : false,
32899 applyConfig : function(config){
32900 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32903 onRender : function(ctr,pos) {
32905 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32906 if(!this.config.split){
32911 var splitEl = Roo.DomHelper.append(ctr.dom, {
32913 id: this.el.id + "-split",
32914 cls: "roo-layout-split roo-layout-split-"+this.position,
32917 /** The SplitBar for this region
32918 * @type Roo.SplitBar */
32919 // does not exist yet...
32920 Roo.log([this.position, this.orientation]);
32922 this.split = new Roo.bootstrap.SplitBar({
32923 dragElement : splitEl,
32924 resizingElement: this.el,
32925 orientation : this.orientation
32928 this.split.on("moved", this.onSplitMove, this);
32929 this.split.useShim = this.config.useShim === true;
32930 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32931 if(this.useSplitTips){
32932 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32934 //if(config.collapsible){
32935 // this.split.el.on("dblclick", this.collapse, this);
32938 if(typeof this.config.minSize != "undefined"){
32939 this.split.minSize = this.config.minSize;
32941 if(typeof this.config.maxSize != "undefined"){
32942 this.split.maxSize = this.config.maxSize;
32944 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32945 this.hideSplitter();
32950 getHMaxSize : function(){
32951 var cmax = this.config.maxSize || 10000;
32952 var center = this.mgr.getRegion("center");
32953 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32956 getVMaxSize : function(){
32957 var cmax = this.config.maxSize || 10000;
32958 var center = this.mgr.getRegion("center");
32959 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32962 onSplitMove : function(split, newSize){
32963 this.fireEvent("resized", this, newSize);
32967 * Returns the {@link Roo.SplitBar} for this region.
32968 * @return {Roo.SplitBar}
32970 getSplitBar : function(){
32975 this.hideSplitter();
32976 Roo.bootstrap.layout.Split.superclass.hide.call(this);
32979 hideSplitter : function(){
32981 this.split.el.setLocation(-2000,-2000);
32982 this.split.el.hide();
32988 this.split.el.show();
32990 Roo.bootstrap.layout.Split.superclass.show.call(this);
32993 beforeSlide: function(){
32994 if(Roo.isGecko){// firefox overflow auto bug workaround
32995 this.bodyEl.clip();
32997 this.tabs.bodyEl.clip();
32999 if(this.activePanel){
33000 this.activePanel.getEl().clip();
33002 if(this.activePanel.beforeSlide){
33003 this.activePanel.beforeSlide();
33009 afterSlide : function(){
33010 if(Roo.isGecko){// firefox overflow auto bug workaround
33011 this.bodyEl.unclip();
33013 this.tabs.bodyEl.unclip();
33015 if(this.activePanel){
33016 this.activePanel.getEl().unclip();
33017 if(this.activePanel.afterSlide){
33018 this.activePanel.afterSlide();
33024 initAutoHide : function(){
33025 if(this.autoHide !== false){
33026 if(!this.autoHideHd){
33027 var st = new Roo.util.DelayedTask(this.slideIn, this);
33028 this.autoHideHd = {
33029 "mouseout": function(e){
33030 if(!e.within(this.el, true)){
33034 "mouseover" : function(e){
33040 this.el.on(this.autoHideHd);
33044 clearAutoHide : function(){
33045 if(this.autoHide !== false){
33046 this.el.un("mouseout", this.autoHideHd.mouseout);
33047 this.el.un("mouseover", this.autoHideHd.mouseover);
33051 clearMonitor : function(){
33052 Roo.get(document).un("click", this.slideInIf, this);
33055 // these names are backwards but not changed for compat
33056 slideOut : function(){
33057 if(this.isSlid || this.el.hasActiveFx()){
33060 this.isSlid = true;
33061 if(this.collapseBtn){
33062 this.collapseBtn.hide();
33064 this.closeBtnState = this.closeBtn.getStyle('display');
33065 this.closeBtn.hide();
33067 this.stickBtn.show();
33070 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33071 this.beforeSlide();
33072 this.el.setStyle("z-index", 10001);
33073 this.el.slideIn(this.getSlideAnchor(), {
33074 callback: function(){
33076 this.initAutoHide();
33077 Roo.get(document).on("click", this.slideInIf, this);
33078 this.fireEvent("slideshow", this);
33085 afterSlideIn : function(){
33086 this.clearAutoHide();
33087 this.isSlid = false;
33088 this.clearMonitor();
33089 this.el.setStyle("z-index", "");
33090 if(this.collapseBtn){
33091 this.collapseBtn.show();
33093 this.closeBtn.setStyle('display', this.closeBtnState);
33095 this.stickBtn.hide();
33097 this.fireEvent("slidehide", this);
33100 slideIn : function(cb){
33101 if(!this.isSlid || this.el.hasActiveFx()){
33105 this.isSlid = false;
33106 this.beforeSlide();
33107 this.el.slideOut(this.getSlideAnchor(), {
33108 callback: function(){
33109 this.el.setLeftTop(-10000, -10000);
33111 this.afterSlideIn();
33119 slideInIf : function(e){
33120 if(!e.within(this.el)){
33125 animateCollapse : function(){
33126 this.beforeSlide();
33127 this.el.setStyle("z-index", 20000);
33128 var anchor = this.getSlideAnchor();
33129 this.el.slideOut(anchor, {
33130 callback : function(){
33131 this.el.setStyle("z-index", "");
33132 this.collapsedEl.slideIn(anchor, {duration:.3});
33134 this.el.setLocation(-10000,-10000);
33136 this.fireEvent("collapsed", this);
33143 animateExpand : function(){
33144 this.beforeSlide();
33145 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33146 this.el.setStyle("z-index", 20000);
33147 this.collapsedEl.hide({
33150 this.el.slideIn(this.getSlideAnchor(), {
33151 callback : function(){
33152 this.el.setStyle("z-index", "");
33155 this.split.el.show();
33157 this.fireEvent("invalidated", this);
33158 this.fireEvent("expanded", this);
33186 getAnchor : function(){
33187 return this.anchors[this.position];
33190 getCollapseAnchor : function(){
33191 return this.canchors[this.position];
33194 getSlideAnchor : function(){
33195 return this.sanchors[this.position];
33198 getAlignAdj : function(){
33199 var cm = this.cmargins;
33200 switch(this.position){
33216 getExpandAdj : function(){
33217 var c = this.collapsedEl, cm = this.cmargins;
33218 switch(this.position){
33220 return [-(cm.right+c.getWidth()+cm.left), 0];
33223 return [cm.right+c.getWidth()+cm.left, 0];
33226 return [0, -(cm.top+cm.bottom+c.getHeight())];
33229 return [0, cm.top+cm.bottom+c.getHeight()];
33235 * Ext JS Library 1.1.1
33236 * Copyright(c) 2006-2007, Ext JS, LLC.
33238 * Originally Released Under LGPL - original licence link has changed is not relivant.
33241 * <script type="text/javascript">
33244 * These classes are private internal classes
33246 Roo.bootstrap.layout.Center = function(config){
33247 config.region = "center";
33248 Roo.bootstrap.layout.Region.call(this, config);
33249 this.visible = true;
33250 this.minWidth = config.minWidth || 20;
33251 this.minHeight = config.minHeight || 20;
33254 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33256 // center panel can't be hidden
33260 // center panel can't be hidden
33263 getMinWidth: function(){
33264 return this.minWidth;
33267 getMinHeight: function(){
33268 return this.minHeight;
33281 Roo.bootstrap.layout.North = function(config)
33283 config.region = 'north';
33284 config.cursor = 'n-resize';
33286 Roo.bootstrap.layout.Split.call(this, config);
33290 this.split.placement = Roo.bootstrap.SplitBar.TOP;
33291 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33292 this.split.el.addClass("roo-layout-split-v");
33294 var size = config.initialSize || config.height;
33295 if(typeof size != "undefined"){
33296 this.el.setHeight(size);
33299 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33301 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33305 getBox : function(){
33306 if(this.collapsed){
33307 return this.collapsedEl.getBox();
33309 var box = this.el.getBox();
33311 box.height += this.split.el.getHeight();
33316 updateBox : function(box){
33317 if(this.split && !this.collapsed){
33318 box.height -= this.split.el.getHeight();
33319 this.split.el.setLeft(box.x);
33320 this.split.el.setTop(box.y+box.height);
33321 this.split.el.setWidth(box.width);
33323 if(this.collapsed){
33324 this.updateBody(box.width, null);
33326 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33334 Roo.bootstrap.layout.South = function(config){
33335 config.region = 'south';
33336 config.cursor = 's-resize';
33337 Roo.bootstrap.layout.Split.call(this, config);
33339 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33340 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33341 this.split.el.addClass("roo-layout-split-v");
33343 var size = config.initialSize || config.height;
33344 if(typeof size != "undefined"){
33345 this.el.setHeight(size);
33349 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33350 orientation: Roo.bootstrap.SplitBar.VERTICAL,
33351 getBox : function(){
33352 if(this.collapsed){
33353 return this.collapsedEl.getBox();
33355 var box = this.el.getBox();
33357 var sh = this.split.el.getHeight();
33364 updateBox : function(box){
33365 if(this.split && !this.collapsed){
33366 var sh = this.split.el.getHeight();
33369 this.split.el.setLeft(box.x);
33370 this.split.el.setTop(box.y-sh);
33371 this.split.el.setWidth(box.width);
33373 if(this.collapsed){
33374 this.updateBody(box.width, null);
33376 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33380 Roo.bootstrap.layout.East = function(config){
33381 config.region = "east";
33382 config.cursor = "e-resize";
33383 Roo.bootstrap.layout.Split.call(this, config);
33385 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33386 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33387 this.split.el.addClass("roo-layout-split-h");
33389 var size = config.initialSize || config.width;
33390 if(typeof size != "undefined"){
33391 this.el.setWidth(size);
33394 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33395 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33396 getBox : function(){
33397 if(this.collapsed){
33398 return this.collapsedEl.getBox();
33400 var box = this.el.getBox();
33402 var sw = this.split.el.getWidth();
33409 updateBox : function(box){
33410 if(this.split && !this.collapsed){
33411 var sw = this.split.el.getWidth();
33413 this.split.el.setLeft(box.x);
33414 this.split.el.setTop(box.y);
33415 this.split.el.setHeight(box.height);
33418 if(this.collapsed){
33419 this.updateBody(null, box.height);
33421 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33425 Roo.bootstrap.layout.West = function(config){
33426 config.region = "west";
33427 config.cursor = "w-resize";
33429 Roo.bootstrap.layout.Split.call(this, config);
33431 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33432 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33433 this.split.el.addClass("roo-layout-split-h");
33437 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33438 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33440 onRender: function(ctr, pos)
33442 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33443 var size = this.config.initialSize || this.config.width;
33444 if(typeof size != "undefined"){
33445 this.el.setWidth(size);
33449 getBox : function(){
33450 if(this.collapsed){
33451 return this.collapsedEl.getBox();
33453 var box = this.el.getBox();
33455 box.width += this.split.el.getWidth();
33460 updateBox : function(box){
33461 if(this.split && !this.collapsed){
33462 var sw = this.split.el.getWidth();
33464 this.split.el.setLeft(box.x+box.width);
33465 this.split.el.setTop(box.y);
33466 this.split.el.setHeight(box.height);
33468 if(this.collapsed){
33469 this.updateBody(null, box.height);
33471 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33474 Roo.namespace("Roo.bootstrap.panel");/*
33476 * Ext JS Library 1.1.1
33477 * Copyright(c) 2006-2007, Ext JS, LLC.
33479 * Originally Released Under LGPL - original licence link has changed is not relivant.
33482 * <script type="text/javascript">
33485 * @class Roo.ContentPanel
33486 * @extends Roo.util.Observable
33487 * A basic ContentPanel element.
33488 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
33489 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
33490 * @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
33491 * @cfg {Boolean} closable True if the panel can be closed/removed
33492 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
33493 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33494 * @cfg {Toolbar} toolbar A toolbar for this panel
33495 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
33496 * @cfg {String} title The title for this panel
33497 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33498 * @cfg {String} url Calls {@link #setUrl} with this value
33499 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33500 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
33501 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
33502 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
33505 * Create a new ContentPanel.
33506 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33507 * @param {String/Object} config A string to set only the title or a config object
33508 * @param {String} content (optional) Set the HTML content for this panel
33509 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33511 Roo.bootstrap.panel.Content = function( config){
33513 var el = config.el;
33514 var content = config.content;
33516 if(config.autoCreate){ // xtype is available if this is called from factory
33519 this.el = Roo.get(el);
33520 if(!this.el && config && config.autoCreate){
33521 if(typeof config.autoCreate == "object"){
33522 if(!config.autoCreate.id){
33523 config.autoCreate.id = config.id||el;
33525 this.el = Roo.DomHelper.append(document.body,
33526 config.autoCreate, true);
33528 var elcfg = { tag: "div",
33529 cls: "roo-layout-inactive-content",
33533 elcfg.html = config.html;
33537 this.el = Roo.DomHelper.append(document.body, elcfg , true);
33540 this.closable = false;
33541 this.loaded = false;
33542 this.active = false;
33545 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33547 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33549 this.wrapEl = this.el.wrap();
33551 if (config.toolbar.items) {
33552 ti = config.toolbar.items ;
33553 delete config.toolbar.items ;
33557 this.toolbar.render(this.wrapEl, 'before');
33558 for(var i =0;i < ti.length;i++) {
33559 // Roo.log(['add child', items[i]]);
33560 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33562 this.toolbar.items = nitems;
33563 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33564 delete config.toolbar;
33568 // xtype created footer. - not sure if will work as we normally have to render first..
33569 if (this.footer && !this.footer.el && this.footer.xtype) {
33570 if (!this.wrapEl) {
33571 this.wrapEl = this.el.wrap();
33574 this.footer.container = this.wrapEl.createChild();
33576 this.footer = Roo.factory(this.footer, Roo);
33581 if(typeof config == "string"){
33582 this.title = config;
33584 Roo.apply(this, config);
33588 this.resizeEl = Roo.get(this.resizeEl, true);
33590 this.resizeEl = this.el;
33592 // handle view.xtype
33600 * Fires when this panel is activated.
33601 * @param {Roo.ContentPanel} this
33605 * @event deactivate
33606 * Fires when this panel is activated.
33607 * @param {Roo.ContentPanel} this
33609 "deactivate" : true,
33613 * Fires when this panel is resized if fitToFrame is true.
33614 * @param {Roo.ContentPanel} this
33615 * @param {Number} width The width after any component adjustments
33616 * @param {Number} height The height after any component adjustments
33622 * Fires when this tab is created
33623 * @param {Roo.ContentPanel} this
33634 if(this.autoScroll){
33635 this.resizeEl.setStyle("overflow", "auto");
33637 // fix randome scrolling
33638 //this.el.on('scroll', function() {
33639 // Roo.log('fix random scolling');
33640 // this.scrollTo('top',0);
33643 content = content || this.content;
33645 this.setContent(content);
33647 if(config && config.url){
33648 this.setUrl(this.url, this.params, this.loadOnce);
33653 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33655 if (this.view && typeof(this.view.xtype) != 'undefined') {
33656 this.view.el = this.el.appendChild(document.createElement("div"));
33657 this.view = Roo.factory(this.view);
33658 this.view.render && this.view.render(false, '');
33662 this.fireEvent('render', this);
33665 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33667 setRegion : function(region){
33668 this.region = region;
33670 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33672 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33677 * Returns the toolbar for this Panel if one was configured.
33678 * @return {Roo.Toolbar}
33680 getToolbar : function(){
33681 return this.toolbar;
33684 setActiveState : function(active){
33685 this.active = active;
33687 this.fireEvent("deactivate", this);
33689 this.fireEvent("activate", this);
33693 * Updates this panel's element
33694 * @param {String} content The new content
33695 * @param {Boolean} loadScripts (optional) true to look for and process scripts
33697 setContent : function(content, loadScripts){
33698 this.el.update(content, loadScripts);
33701 ignoreResize : function(w, h){
33702 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33705 this.lastSize = {width: w, height: h};
33710 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33711 * @return {Roo.UpdateManager} The UpdateManager
33713 getUpdateManager : function(){
33714 return this.el.getUpdateManager();
33717 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33718 * @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:
33721 url: "your-url.php",
33722 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33723 callback: yourFunction,
33724 scope: yourObject, //(optional scope)
33727 text: "Loading...",
33732 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33733 * 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.
33734 * @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}
33735 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33736 * @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.
33737 * @return {Roo.ContentPanel} this
33740 var um = this.el.getUpdateManager();
33741 um.update.apply(um, arguments);
33747 * 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.
33748 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33749 * @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)
33750 * @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)
33751 * @return {Roo.UpdateManager} The UpdateManager
33753 setUrl : function(url, params, loadOnce){
33754 if(this.refreshDelegate){
33755 this.removeListener("activate", this.refreshDelegate);
33757 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33758 this.on("activate", this.refreshDelegate);
33759 return this.el.getUpdateManager();
33762 _handleRefresh : function(url, params, loadOnce){
33763 if(!loadOnce || !this.loaded){
33764 var updater = this.el.getUpdateManager();
33765 updater.update(url, params, this._setLoaded.createDelegate(this));
33769 _setLoaded : function(){
33770 this.loaded = true;
33774 * Returns this panel's id
33777 getId : function(){
33782 * Returns this panel's element - used by regiosn to add.
33783 * @return {Roo.Element}
33785 getEl : function(){
33786 return this.wrapEl || this.el;
33791 adjustForComponents : function(width, height)
33793 //Roo.log('adjustForComponents ');
33794 if(this.resizeEl != this.el){
33795 width -= this.el.getFrameWidth('lr');
33796 height -= this.el.getFrameWidth('tb');
33799 var te = this.toolbar.getEl();
33800 height -= te.getHeight();
33801 te.setWidth(width);
33804 var te = this.footer.getEl();
33805 Roo.log("footer:" + te.getHeight());
33807 height -= te.getHeight();
33808 te.setWidth(width);
33812 if(this.adjustments){
33813 width += this.adjustments[0];
33814 height += this.adjustments[1];
33816 return {"width": width, "height": height};
33819 setSize : function(width, height){
33820 if(this.fitToFrame && !this.ignoreResize(width, height)){
33821 if(this.fitContainer && this.resizeEl != this.el){
33822 this.el.setSize(width, height);
33824 var size = this.adjustForComponents(width, height);
33825 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33826 this.fireEvent('resize', this, size.width, size.height);
33831 * Returns this panel's title
33834 getTitle : function(){
33839 * Set this panel's title
33840 * @param {String} title
33842 setTitle : function(title){
33843 this.title = title;
33845 this.region.updatePanelTitle(this, title);
33850 * Returns true is this panel was configured to be closable
33851 * @return {Boolean}
33853 isClosable : function(){
33854 return this.closable;
33857 beforeSlide : function(){
33859 this.resizeEl.clip();
33862 afterSlide : function(){
33864 this.resizeEl.unclip();
33868 * Force a content refresh from the URL specified in the {@link #setUrl} method.
33869 * Will fail silently if the {@link #setUrl} method has not been called.
33870 * This does not activate the panel, just updates its content.
33872 refresh : function(){
33873 if(this.refreshDelegate){
33874 this.loaded = false;
33875 this.refreshDelegate();
33880 * Destroys this panel
33882 destroy : function(){
33883 this.el.removeAllListeners();
33884 var tempEl = document.createElement("span");
33885 tempEl.appendChild(this.el.dom);
33886 tempEl.innerHTML = "";
33892 * form - if the content panel contains a form - this is a reference to it.
33893 * @type {Roo.form.Form}
33897 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33898 * This contains a reference to it.
33904 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33914 * @param {Object} cfg Xtype definition of item to add.
33918 getChildContainer: function () {
33919 return this.getEl();
33924 var ret = new Roo.factory(cfg);
33929 if (cfg.xtype.match(/^Form$/)) {
33932 //if (this.footer) {
33933 // el = this.footer.container.insertSibling(false, 'before');
33935 el = this.el.createChild();
33938 this.form = new Roo.form.Form(cfg);
33941 if ( this.form.allItems.length) {
33942 this.form.render(el.dom);
33946 // should only have one of theses..
33947 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33948 // views.. should not be just added - used named prop 'view''
33950 cfg.el = this.el.appendChild(document.createElement("div"));
33953 var ret = new Roo.factory(cfg);
33955 ret.render && ret.render(false, ''); // render blank..
33965 * @class Roo.bootstrap.panel.Grid
33966 * @extends Roo.bootstrap.panel.Content
33968 * Create a new GridPanel.
33969 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33970 * @param {Object} config A the config object
33976 Roo.bootstrap.panel.Grid = function(config)
33980 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33981 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33983 config.el = this.wrapper;
33984 //this.el = this.wrapper;
33986 if (config.container) {
33987 // ctor'ed from a Border/panel.grid
33990 this.wrapper.setStyle("overflow", "hidden");
33991 this.wrapper.addClass('roo-grid-container');
33996 if(config.toolbar){
33997 var tool_el = this.wrapper.createChild();
33998 this.toolbar = Roo.factory(config.toolbar);
34000 if (config.toolbar.items) {
34001 ti = config.toolbar.items ;
34002 delete config.toolbar.items ;
34006 this.toolbar.render(tool_el);
34007 for(var i =0;i < ti.length;i++) {
34008 // Roo.log(['add child', items[i]]);
34009 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34011 this.toolbar.items = nitems;
34013 delete config.toolbar;
34016 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34017 config.grid.scrollBody = true;;
34018 config.grid.monitorWindowResize = false; // turn off autosizing
34019 config.grid.autoHeight = false;
34020 config.grid.autoWidth = false;
34022 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34024 if (config.background) {
34025 // render grid on panel activation (if panel background)
34026 this.on('activate', function(gp) {
34027 if (!gp.grid.rendered) {
34028 gp.grid.render(el);
34029 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34035 this.grid.render(this.wrapper);
34036 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
34039 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34040 // ??? needed ??? config.el = this.wrapper;
34045 // xtype created footer. - not sure if will work as we normally have to render first..
34046 if (this.footer && !this.footer.el && this.footer.xtype) {
34048 var ctr = this.grid.getView().getFooterPanel(true);
34049 this.footer.dataSource = this.grid.dataSource;
34050 this.footer = Roo.factory(this.footer, Roo);
34051 this.footer.render(ctr);
34061 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34062 getId : function(){
34063 return this.grid.id;
34067 * Returns the grid for this panel
34068 * @return {Roo.bootstrap.Table}
34070 getGrid : function(){
34074 setSize : function(width, height){
34075 if(!this.ignoreResize(width, height)){
34076 var grid = this.grid;
34077 var size = this.adjustForComponents(width, height);
34078 var gridel = grid.getGridEl();
34079 gridel.setSize(size.width, size.height);
34081 var thd = grid.getGridEl().select('thead',true).first();
34082 var tbd = grid.getGridEl().select('tbody', true).first();
34084 tbd.setSize(width, height - thd.getHeight());
34093 beforeSlide : function(){
34094 this.grid.getView().scroller.clip();
34097 afterSlide : function(){
34098 this.grid.getView().scroller.unclip();
34101 destroy : function(){
34102 this.grid.destroy();
34104 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
34109 * @class Roo.bootstrap.panel.Nest
34110 * @extends Roo.bootstrap.panel.Content
34112 * Create a new Panel, that can contain a layout.Border.
34115 * @param {Roo.BorderLayout} layout The layout for this panel
34116 * @param {String/Object} config A string to set only the title or a config object
34118 Roo.bootstrap.panel.Nest = function(config)
34120 // construct with only one argument..
34121 /* FIXME - implement nicer consturctors
34122 if (layout.layout) {
34124 layout = config.layout;
34125 delete config.layout;
34127 if (layout.xtype && !layout.getEl) {
34128 // then layout needs constructing..
34129 layout = Roo.factory(layout, Roo);
34133 config.el = config.layout.getEl();
34135 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34137 config.layout.monitorWindowResize = false; // turn off autosizing
34138 this.layout = config.layout;
34139 this.layout.getEl().addClass("roo-layout-nested-layout");
34146 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34148 setSize : function(width, height){
34149 if(!this.ignoreResize(width, height)){
34150 var size = this.adjustForComponents(width, height);
34151 var el = this.layout.getEl();
34152 el.setSize(size.width, size.height);
34153 var touch = el.dom.offsetWidth;
34154 this.layout.layout();
34155 // ie requires a double layout on the first pass
34156 if(Roo.isIE && !this.initialized){
34157 this.initialized = true;
34158 this.layout.layout();
34163 // activate all subpanels if not currently active..
34165 setActiveState : function(active){
34166 this.active = active;
34168 this.fireEvent("deactivate", this);
34172 this.fireEvent("activate", this);
34173 // not sure if this should happen before or after..
34174 if (!this.layout) {
34175 return; // should not happen..
34178 for (var r in this.layout.regions) {
34179 reg = this.layout.getRegion(r);
34180 if (reg.getActivePanel()) {
34181 //reg.showPanel(reg.getActivePanel()); // force it to activate..
34182 reg.setActivePanel(reg.getActivePanel());
34185 if (!reg.panels.length) {
34188 reg.showPanel(reg.getPanel(0));
34197 * Returns the nested BorderLayout for this panel
34198 * @return {Roo.BorderLayout}
34200 getLayout : function(){
34201 return this.layout;
34205 * Adds a xtype elements to the layout of the nested panel
34209 xtype : 'ContentPanel',
34216 xtype : 'NestedLayoutPanel',
34222 items : [ ... list of content panels or nested layout panels.. ]
34226 * @param {Object} cfg Xtype definition of item to add.
34228 addxtype : function(cfg) {
34229 return this.layout.addxtype(cfg);
34234 * Ext JS Library 1.1.1
34235 * Copyright(c) 2006-2007, Ext JS, LLC.
34237 * Originally Released Under LGPL - original licence link has changed is not relivant.
34240 * <script type="text/javascript">
34243 * @class Roo.TabPanel
34244 * @extends Roo.util.Observable
34245 * A lightweight tab container.
34249 // basic tabs 1, built from existing content
34250 var tabs = new Roo.TabPanel("tabs1");
34251 tabs.addTab("script", "View Script");
34252 tabs.addTab("markup", "View Markup");
34253 tabs.activate("script");
34255 // more advanced tabs, built from javascript
34256 var jtabs = new Roo.TabPanel("jtabs");
34257 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34259 // set up the UpdateManager
34260 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34261 var updater = tab2.getUpdateManager();
34262 updater.setDefaultUrl("ajax1.htm");
34263 tab2.on('activate', updater.refresh, updater, true);
34265 // Use setUrl for Ajax loading
34266 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34267 tab3.setUrl("ajax2.htm", null, true);
34270 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34273 jtabs.activate("jtabs-1");
34276 * Create a new TabPanel.
34277 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34278 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34280 Roo.bootstrap.panel.Tabs = function(config){
34282 * The container element for this TabPanel.
34283 * @type Roo.Element
34285 this.el = Roo.get(config.el);
34288 if(typeof config == "boolean"){
34289 this.tabPosition = config ? "bottom" : "top";
34291 Roo.apply(this, config);
34295 if(this.tabPosition == "bottom"){
34296 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34297 this.el.addClass("roo-tabs-bottom");
34299 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34300 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34301 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34303 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34305 if(this.tabPosition != "bottom"){
34306 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34307 * @type Roo.Element
34309 this.bodyEl = Roo.get(this.createBody(this.el.dom));
34310 this.el.addClass("roo-tabs-top");
34314 this.bodyEl.setStyle("position", "relative");
34316 this.active = null;
34317 this.activateDelegate = this.activate.createDelegate(this);
34322 * Fires when the active tab changes
34323 * @param {Roo.TabPanel} this
34324 * @param {Roo.TabPanelItem} activePanel The new active tab
34328 * @event beforetabchange
34329 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34330 * @param {Roo.TabPanel} this
34331 * @param {Object} e Set cancel to true on this object to cancel the tab change
34332 * @param {Roo.TabPanelItem} tab The tab being changed to
34334 "beforetabchange" : true
34337 Roo.EventManager.onWindowResize(this.onResize, this);
34338 this.cpad = this.el.getPadding("lr");
34339 this.hiddenCount = 0;
34342 // toolbar on the tabbar support...
34343 if (this.toolbar) {
34344 alert("no toolbar support yet");
34345 this.toolbar = false;
34347 var tcfg = this.toolbar;
34348 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
34349 this.toolbar = new Roo.Toolbar(tcfg);
34350 if (Roo.isSafari) {
34351 var tbl = tcfg.container.child('table', true);
34352 tbl.setAttribute('width', '100%');
34360 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34363 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34365 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34367 tabPosition : "top",
34369 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34371 currentTabWidth : 0,
34373 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34377 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34381 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34383 preferredTabWidth : 175,
34385 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34387 resizeTabs : false,
34389 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34391 monitorResize : true,
34393 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
34398 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34399 * @param {String} id The id of the div to use <b>or create</b>
34400 * @param {String} text The text for the tab
34401 * @param {String} content (optional) Content to put in the TabPanelItem body
34402 * @param {Boolean} closable (optional) True to create a close icon on the tab
34403 * @return {Roo.TabPanelItem} The created TabPanelItem
34405 addTab : function(id, text, content, closable)
34407 var item = new Roo.bootstrap.panel.TabItem({
34411 closable : closable
34413 this.addTabItem(item);
34415 item.setContent(content);
34421 * Returns the {@link Roo.TabPanelItem} with the specified id/index
34422 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34423 * @return {Roo.TabPanelItem}
34425 getTab : function(id){
34426 return this.items[id];
34430 * Hides the {@link Roo.TabPanelItem} with the specified id/index
34431 * @param {String/Number} id The id or index of the TabPanelItem to hide.
34433 hideTab : function(id){
34434 var t = this.items[id];
34437 this.hiddenCount++;
34438 this.autoSizeTabs();
34443 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34444 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34446 unhideTab : function(id){
34447 var t = this.items[id];
34449 t.setHidden(false);
34450 this.hiddenCount--;
34451 this.autoSizeTabs();
34456 * Adds an existing {@link Roo.TabPanelItem}.
34457 * @param {Roo.TabPanelItem} item The TabPanelItem to add
34459 addTabItem : function(item){
34460 this.items[item.id] = item;
34461 this.items.push(item);
34462 // if(this.resizeTabs){
34463 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34464 // this.autoSizeTabs();
34466 // item.autoSize();
34471 * Removes a {@link Roo.TabPanelItem}.
34472 * @param {String/Number} id The id or index of the TabPanelItem to remove.
34474 removeTab : function(id){
34475 var items = this.items;
34476 var tab = items[id];
34477 if(!tab) { return; }
34478 var index = items.indexOf(tab);
34479 if(this.active == tab && items.length > 1){
34480 var newTab = this.getNextAvailable(index);
34485 this.stripEl.dom.removeChild(tab.pnode.dom);
34486 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34487 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34489 items.splice(index, 1);
34490 delete this.items[tab.id];
34491 tab.fireEvent("close", tab);
34492 tab.purgeListeners();
34493 this.autoSizeTabs();
34496 getNextAvailable : function(start){
34497 var items = this.items;
34499 // look for a next tab that will slide over to
34500 // replace the one being removed
34501 while(index < items.length){
34502 var item = items[++index];
34503 if(item && !item.isHidden()){
34507 // if one isn't found select the previous tab (on the left)
34510 var item = items[--index];
34511 if(item && !item.isHidden()){
34519 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34520 * @param {String/Number} id The id or index of the TabPanelItem to disable.
34522 disableTab : function(id){
34523 var tab = this.items[id];
34524 if(tab && this.active != tab){
34530 * Enables a {@link Roo.TabPanelItem} that is disabled.
34531 * @param {String/Number} id The id or index of the TabPanelItem to enable.
34533 enableTab : function(id){
34534 var tab = this.items[id];
34539 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34540 * @param {String/Number} id The id or index of the TabPanelItem to activate.
34541 * @return {Roo.TabPanelItem} The TabPanelItem.
34543 activate : function(id){
34544 var tab = this.items[id];
34548 if(tab == this.active || tab.disabled){
34552 this.fireEvent("beforetabchange", this, e, tab);
34553 if(e.cancel !== true && !tab.disabled){
34555 this.active.hide();
34557 this.active = this.items[id];
34558 this.active.show();
34559 this.fireEvent("tabchange", this, this.active);
34565 * Gets the active {@link Roo.TabPanelItem}.
34566 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34568 getActiveTab : function(){
34569 return this.active;
34573 * Updates the tab body element to fit the height of the container element
34574 * for overflow scrolling
34575 * @param {Number} targetHeight (optional) Override the starting height from the elements height
34577 syncHeight : function(targetHeight){
34578 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34579 var bm = this.bodyEl.getMargins();
34580 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34581 this.bodyEl.setHeight(newHeight);
34585 onResize : function(){
34586 if(this.monitorResize){
34587 this.autoSizeTabs();
34592 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34594 beginUpdate : function(){
34595 this.updating = true;
34599 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34601 endUpdate : function(){
34602 this.updating = false;
34603 this.autoSizeTabs();
34607 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34609 autoSizeTabs : function(){
34610 var count = this.items.length;
34611 var vcount = count - this.hiddenCount;
34612 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34615 var w = Math.max(this.el.getWidth() - this.cpad, 10);
34616 var availWidth = Math.floor(w / vcount);
34617 var b = this.stripBody;
34618 if(b.getWidth() > w){
34619 var tabs = this.items;
34620 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34621 if(availWidth < this.minTabWidth){
34622 /*if(!this.sleft){ // incomplete scrolling code
34623 this.createScrollButtons();
34626 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34629 if(this.currentTabWidth < this.preferredTabWidth){
34630 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34636 * Returns the number of tabs in this TabPanel.
34639 getCount : function(){
34640 return this.items.length;
34644 * Resizes all the tabs to the passed width
34645 * @param {Number} The new width
34647 setTabWidth : function(width){
34648 this.currentTabWidth = width;
34649 for(var i = 0, len = this.items.length; i < len; i++) {
34650 if(!this.items[i].isHidden()) {
34651 this.items[i].setWidth(width);
34657 * Destroys this TabPanel
34658 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34660 destroy : function(removeEl){
34661 Roo.EventManager.removeResizeListener(this.onResize, this);
34662 for(var i = 0, len = this.items.length; i < len; i++){
34663 this.items[i].purgeListeners();
34665 if(removeEl === true){
34666 this.el.update("");
34671 createStrip : function(container)
34673 var strip = document.createElement("nav");
34674 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34675 container.appendChild(strip);
34679 createStripList : function(strip)
34681 // div wrapper for retard IE
34682 // returns the "tr" element.
34683 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34684 //'<div class="x-tabs-strip-wrap">'+
34685 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34686 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34687 return strip.firstChild; //.firstChild.firstChild.firstChild;
34689 createBody : function(container)
34691 var body = document.createElement("div");
34692 Roo.id(body, "tab-body");
34693 //Roo.fly(body).addClass("x-tabs-body");
34694 Roo.fly(body).addClass("tab-content");
34695 container.appendChild(body);
34698 createItemBody :function(bodyEl, id){
34699 var body = Roo.getDom(id);
34701 body = document.createElement("div");
34704 //Roo.fly(body).addClass("x-tabs-item-body");
34705 Roo.fly(body).addClass("tab-pane");
34706 bodyEl.insertBefore(body, bodyEl.firstChild);
34710 createStripElements : function(stripEl, text, closable)
34712 var td = document.createElement("li"); // was td..
34713 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34714 //stripEl.appendChild(td);
34716 td.className = "x-tabs-closable";
34717 if(!this.closeTpl){
34718 this.closeTpl = new Roo.Template(
34719 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34720 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34721 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
34724 var el = this.closeTpl.overwrite(td, {"text": text});
34725 var close = el.getElementsByTagName("div")[0];
34726 var inner = el.getElementsByTagName("em")[0];
34727 return {"el": el, "close": close, "inner": inner};
34730 // not sure what this is..
34732 //this.tabTpl = new Roo.Template(
34733 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34734 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34736 this.tabTpl = new Roo.Template(
34738 '<span unselectable="on"' +
34739 (this.disableTooltips ? '' : ' title="{text}"') +
34740 ' >{text}</span></span></a>'
34744 var el = this.tabTpl.overwrite(td, {"text": text});
34745 var inner = el.getElementsByTagName("span")[0];
34746 return {"el": el, "inner": inner};
34754 * @class Roo.TabPanelItem
34755 * @extends Roo.util.Observable
34756 * Represents an individual item (tab plus body) in a TabPanel.
34757 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34758 * @param {String} id The id of this TabPanelItem
34759 * @param {String} text The text for the tab of this TabPanelItem
34760 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34762 Roo.bootstrap.panel.TabItem = function(config){
34764 * The {@link Roo.TabPanel} this TabPanelItem belongs to
34765 * @type Roo.TabPanel
34767 this.tabPanel = config.panel;
34769 * The id for this TabPanelItem
34772 this.id = config.id;
34774 this.disabled = false;
34776 this.text = config.text;
34778 this.loaded = false;
34779 this.closable = config.closable;
34782 * The body element for this TabPanelItem.
34783 * @type Roo.Element
34785 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34786 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34787 this.bodyEl.setStyle("display", "block");
34788 this.bodyEl.setStyle("zoom", "1");
34789 //this.hideAction();
34791 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34793 this.el = Roo.get(els.el);
34794 this.inner = Roo.get(els.inner, true);
34795 this.textEl = Roo.get(this.el.dom.firstChild, true);
34796 this.pnode = Roo.get(els.el.parentNode, true);
34797 this.el.on("mousedown", this.onTabMouseDown, this);
34798 this.el.on("click", this.onTabClick, this);
34800 if(config.closable){
34801 var c = Roo.get(els.close, true);
34802 c.dom.title = this.closeText;
34803 c.addClassOnOver("close-over");
34804 c.on("click", this.closeClick, this);
34810 * Fires when this tab becomes the active tab.
34811 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34812 * @param {Roo.TabPanelItem} this
34816 * @event beforeclose
34817 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34818 * @param {Roo.TabPanelItem} this
34819 * @param {Object} e Set cancel to true on this object to cancel the close.
34821 "beforeclose": true,
34824 * Fires when this tab is closed.
34825 * @param {Roo.TabPanelItem} this
34829 * @event deactivate
34830 * Fires when this tab is no longer the active tab.
34831 * @param {Roo.TabPanel} tabPanel The parent TabPanel
34832 * @param {Roo.TabPanelItem} this
34834 "deactivate" : true
34836 this.hidden = false;
34838 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34841 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34843 purgeListeners : function(){
34844 Roo.util.Observable.prototype.purgeListeners.call(this);
34845 this.el.removeAllListeners();
34848 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34851 this.pnode.addClass("active");
34854 this.tabPanel.stripWrap.repaint();
34856 this.fireEvent("activate", this.tabPanel, this);
34860 * Returns true if this tab is the active tab.
34861 * @return {Boolean}
34863 isActive : function(){
34864 return this.tabPanel.getActiveTab() == this;
34868 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34871 this.pnode.removeClass("active");
34873 this.fireEvent("deactivate", this.tabPanel, this);
34876 hideAction : function(){
34877 this.bodyEl.hide();
34878 this.bodyEl.setStyle("position", "absolute");
34879 this.bodyEl.setLeft("-20000px");
34880 this.bodyEl.setTop("-20000px");
34883 showAction : function(){
34884 this.bodyEl.setStyle("position", "relative");
34885 this.bodyEl.setTop("");
34886 this.bodyEl.setLeft("");
34887 this.bodyEl.show();
34891 * Set the tooltip for the tab.
34892 * @param {String} tooltip The tab's tooltip
34894 setTooltip : function(text){
34895 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34896 this.textEl.dom.qtip = text;
34897 this.textEl.dom.removeAttribute('title');
34899 this.textEl.dom.title = text;
34903 onTabClick : function(e){
34904 e.preventDefault();
34905 this.tabPanel.activate(this.id);
34908 onTabMouseDown : function(e){
34909 e.preventDefault();
34910 this.tabPanel.activate(this.id);
34913 getWidth : function(){
34914 return this.inner.getWidth();
34917 setWidth : function(width){
34918 var iwidth = width - this.pnode.getPadding("lr");
34919 this.inner.setWidth(iwidth);
34920 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34921 this.pnode.setWidth(width);
34925 * Show or hide the tab
34926 * @param {Boolean} hidden True to hide or false to show.
34928 setHidden : function(hidden){
34929 this.hidden = hidden;
34930 this.pnode.setStyle("display", hidden ? "none" : "");
34934 * Returns true if this tab is "hidden"
34935 * @return {Boolean}
34937 isHidden : function(){
34938 return this.hidden;
34942 * Returns the text for this tab
34945 getText : function(){
34949 autoSize : function(){
34950 //this.el.beginMeasure();
34951 this.textEl.setWidth(1);
34953 * #2804 [new] Tabs in Roojs
34954 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34956 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34957 //this.el.endMeasure();
34961 * Sets the text for the tab (Note: this also sets the tooltip text)
34962 * @param {String} text The tab's text and tooltip
34964 setText : function(text){
34966 this.textEl.update(text);
34967 this.setTooltip(text);
34968 //if(!this.tabPanel.resizeTabs){
34969 // this.autoSize();
34973 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34975 activate : function(){
34976 this.tabPanel.activate(this.id);
34980 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34982 disable : function(){
34983 if(this.tabPanel.active != this){
34984 this.disabled = true;
34985 this.pnode.addClass("disabled");
34990 * Enables this TabPanelItem if it was previously disabled.
34992 enable : function(){
34993 this.disabled = false;
34994 this.pnode.removeClass("disabled");
34998 * Sets the content for this TabPanelItem.
34999 * @param {String} content The content
35000 * @param {Boolean} loadScripts true to look for and load scripts
35002 setContent : function(content, loadScripts){
35003 this.bodyEl.update(content, loadScripts);
35007 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35008 * @return {Roo.UpdateManager} The UpdateManager
35010 getUpdateManager : function(){
35011 return this.bodyEl.getUpdateManager();
35015 * Set a URL to be used to load the content for this TabPanelItem.
35016 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35017 * @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)
35018 * @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)
35019 * @return {Roo.UpdateManager} The UpdateManager
35021 setUrl : function(url, params, loadOnce){
35022 if(this.refreshDelegate){
35023 this.un('activate', this.refreshDelegate);
35025 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35026 this.on("activate", this.refreshDelegate);
35027 return this.bodyEl.getUpdateManager();
35031 _handleRefresh : function(url, params, loadOnce){
35032 if(!loadOnce || !this.loaded){
35033 var updater = this.bodyEl.getUpdateManager();
35034 updater.update(url, params, this._setLoaded.createDelegate(this));
35039 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
35040 * Will fail silently if the setUrl method has not been called.
35041 * This does not activate the panel, just updates its content.
35043 refresh : function(){
35044 if(this.refreshDelegate){
35045 this.loaded = false;
35046 this.refreshDelegate();
35051 _setLoaded : function(){
35052 this.loaded = true;
35056 closeClick : function(e){
35059 this.fireEvent("beforeclose", this, o);
35060 if(o.cancel !== true){
35061 this.tabPanel.removeTab(this.id);
35065 * The text displayed in the tooltip for the close icon.
35068 closeText : "Close this tab"